diff -Nru bambootracker-0.4.5+git20121209/.appveyor.yml bambootracker-0.4.6/.appveyor.yml --- bambootracker-0.4.5+git20121209/.appveyor.yml 2020-12-08 14:05:28.000000000 +0000 +++ bambootracker-0.4.6/.appveyor.yml 2021-02-11 14:37:17.000000000 +0000 @@ -3,25 +3,23 @@ #---------------------------------# # version format -version: 0.4.4.{build} +version: 0.4.5.{build} # branches to build branches: # whitelist only: - master - -# Do not build on tags -skip_tags: true + - /v\d*\.\d*\.\d*/ # Skipping commits with particular message or from specific user skip_commits: - message: /Created.*\.(png|jpg|jpeg|bmp|gif|md)/ # Regex for matching commit message - files: - - '*.md' - - '*.txt' - - 'LICENSE' - - '.gitignore' + message: /Created.*\.(png|jpg|jpeg|bmp|gif|md)/ # Regex for matching commit message + files: + - '*.md' + - '*.txt' + - 'LICENSE' + - '.gitignore' #---------------------------------# # environment configuration # @@ -41,7 +39,8 @@ PLATFORM: windows DEST: BambooTracker-v%APPVEYOR_BUILD_VERSION%-dev-windows.zip MAKE: mingw32-make - # Windows XP + RELEASE_BUILD: false + # Windows XP (Debug) - APPVEYOR_JOB_NAME: for Windows XP APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2013 COMPILER: C:\Qt\Tools\mingw492_32\bin @@ -49,11 +48,36 @@ PLATFORM: windows-xp DEST: BambooTracker-v%APPVEYOR_BUILD_VERSION%-dev-windows-xp.zip MAKE: mingw32-make + RELEASE_BUILD: false + # Windows XP (Release) + - APPVEYOR_JOB_NAME: for Windows XP (Release) + APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2013 + COMPILER: C:\Qt\Tools\mingw492_32\bin + QT: C:\Qt\5.5\mingw492_32\bin + PLATFORM: windows-xp + DEST: BambooTracker-v%APPVEYOR_BUILD_VERSION%-windows-xp.zip + MAKE: mingw32-make + RELEASE_BUILD: true - APPVEYOR_JOB_NAME: for macOS APPVEYOR_BUILD_WORKER_IMAGE: macOS-Mojave PLATFORM: macos DEST: BambooTracker-v$APPVEYOR_BUILD_VERSION-dev-macos.zip MAKE: make + RELEASE_BUILD: false + +for: +- + # Debug + matrix: + only: + - RELEASE_BUILD: false + skip_tags: true +- + # Release + matrix: + only: + - RELEASE_BUILD: true + skip_non_tags: true # scripts that run after cloning repository install: @@ -71,26 +95,27 @@ before_build: - ps: $env:Path = "$env:QT;$env:COMPILER;$env:Path" - sh: | - brew install qt jack p7zip + brew install qt pkg-config jack p7zip export PATH="/usr/local/opt/qt/bin:$PATH" - export JACKLIB="/usr/local/opt/jack/lib" - export JACKINC="/usr/local/opt/jack/include" - - cd BambooTracker + export PKG_CONFIG_PATH="/usr/local/opt/jack/lib/pkgconfig"${PKG_CONFIG_PATH:+':'}$PKG_CONFIG_PATH # to run your custom scripts instead of automatic MSBuild build_script: - ps: | - qmake.exe BambooTracker.pro PREFIX=..\target CONFIG+=debug CONFIG+=console CONFIG+=nostrip + $QMAKE_CONFIGS = if ($env:RELEASE_BUILD -ieq "true") { "CONFIG+=release CONFIG-=debug" } else { "CONFIG-=release CONFIG+=debug CONFIG+=console CONFIG+=nostrip" } + echo $QMAKE_CONFIGS + Invoke-Expression ("qmake.exe Project.pro PREFIX=$pwd\target " + $QMAKE_CONFIGS) + Invoke-Expression ($env:MAKE + " qmake_all") Invoke-Expression ($env:MAKE + " -j2") - sh: | - qmake BambooTracker.pro PREFIX=../target CONFIG+=debug CONFIG+=console CONFIG+=nostrip LIBS+=-L"$JACKLIB" INCLUDEPATH+="$JACKINC" CONFIG+=install_flat + qmake Project.pro PREFIX="$APPVEYOR_BUILD_FOLDER"/target CONFIG-=release CONFIG+=debug CONFIG+=console CONFIG+=nostrip CONFIG+=install_flat CONFIG+=use_jack + $MAKE qmake_all $MAKE -j2 # scripts to run after build (working directory and environment changes are persisted from the previous steps) after_build: - ps: Invoke-Expression ($env:MAKE + " install") - sh: $MAKE install - - cd .. - cd target # Port of scripts/package_windows.sh to PowerShell - ps: | @@ -107,8 +132,9 @@ - sh: bash ../scripts/package_osx.sh - cd .. - ps: | - mv target BambooTracker-v"$env:APPVEYOR_BUILD_VERSION"-dev-"$env:PLATFORM" - 7z a -tzip "$env:DEST" BambooTracker-v"$env:APPVEYOR_BUILD_VERSION"-dev-"$env:PLATFORM" + $DEV_LAB = if ($env:RELEASE_BUILD -ieq "true") { "-" } else { "-dev-" } + mv target BambooTracker-v"$env:APPVEYOR_BUILD_VERSION$DEV_LAB$env:PLATFORM" + 7z a -tzip "$env:DEST" BambooTracker-v"$env:APPVEYOR_BUILD_VERSION$DEV_LAB$env:PLATFORM" - sh: | mv target BambooTracker-v"$APPVEYOR_BUILD_VERSION"-dev-"$PLATFORM" 7z a -tzip "$DEST" BambooTracker-v"$APPVEYOR_BUILD_VERSION"-dev-"$PLATFORM" diff -Nru bambootracker-0.4.5+git20121209/BambooTracker/audio/audio_stream_rtaudio.cpp bambootracker-0.4.6/BambooTracker/audio/audio_stream_rtaudio.cpp --- bambootracker-0.4.5+git20121209/BambooTracker/audio/audio_stream_rtaudio.cpp 2020-12-08 14:05:28.000000000 +0000 +++ bambootracker-0.4.6/BambooTracker/audio/audio_stream_rtaudio.cpp 2021-02-11 14:37:17.000000000 +0000 @@ -26,7 +26,7 @@ #include "audio_stream_rtaudio.hpp" #include #include -#include "RtAudio/RtAudio.hpp" +#include "RtAudio.h" AudioStreamRtAudio::AudioStreamRtAudio(QObject* parent) : AudioStream(parent) diff -Nru bambootracker-0.4.5+git20121209/BambooTracker/audio/RtAudio/RtAudio.cpp bambootracker-0.4.6/BambooTracker/audio/RtAudio/RtAudio.cpp --- bambootracker-0.4.5+git20121209/BambooTracker/audio/RtAudio/RtAudio.cpp 2020-12-08 14:05:28.000000000 +0000 +++ bambootracker-0.4.6/BambooTracker/audio/RtAudio/RtAudio.cpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,10645 +0,0 @@ -/************************************************************************/ -/*! \class RtAudio - \brief Realtime audio i/o C++ classes. - - RtAudio provides a common API (Application Programming Interface) - for realtime audio input/output across Linux (native ALSA, Jack, - and OSS), Macintosh OS X (CoreAudio and Jack), and Windows - (DirectSound, ASIO and WASAPI) operating systems. - - RtAudio GitHub site: https://github.com/thestk/rtaudio - RtAudio WWW site: http://www.music.mcgill.ca/~gary/rtaudio/ - - RtAudio: realtime audio i/o C++ classes - Copyright (c) 2001-2019 Gary P. Scavone - - Permission is hereby granted, free of charge, to any person - obtaining a copy of this software and associated documentation files - (the "Software"), to deal in the Software without restriction, - including without limitation the rights to use, copy, modify, merge, - publish, distribute, sublicense, and/or sell copies of the Software, - and to permit persons to whom the Software is furnished to do so, - subject to the following conditions: - - The above copyright notice and this permission notice shall be - included in all copies or substantial portions of the Software. - - Any person wishing to distribute modifications to the Software is - asked to send the modifications to the original developer so that - they can be incorporated into the canonical version. This is, - however, not a binding provision of this license. - - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. - IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR - ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF - CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION - WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -*/ -/************************************************************************/ - -// RtAudio: Version 5.1.0 - -#include "RtAudio.hpp" -#include -#include -#include -#include -#include -#include - -// Static variable definitions. -const unsigned int RtApi::MAX_SAMPLE_RATES = 14; -const unsigned int RtApi::SAMPLE_RATES[] = { - 4000, 5512, 8000, 9600, 11025, 16000, 22050, - 32000, 44100, 48000, 88200, 96000, 176400, 192000 -}; - -#if defined(__WINDOWS_DS__) || defined(__WINDOWS_ASIO__) || defined(__WINDOWS_WASAPI__) - #define MUTEX_INITIALIZE(A) InitializeCriticalSection(A) - #define MUTEX_DESTROY(A) DeleteCriticalSection(A) - #define MUTEX_LOCK(A) EnterCriticalSection(A) - #define MUTEX_UNLOCK(A) LeaveCriticalSection(A) - - #include "tchar.h" - -/********************/ -#if !defined(DECL_MAYBE_UNUSED) && defined(__GNUC__) -#define DECL_MAYBE_UNUSED __attribute__((unused)) -#elif !defined(DECL_MAYBE_UNUSED) -#define DECL_MAYBE_UNUSED -#endif -/********************/ - -DECL_MAYBE_UNUSED - static std::string convertCharPointerToStdString(const char *text) - { - return std::string(text); - } - - static std::string convertCharPointerToStdString(const wchar_t *text) - { - int length = WideCharToMultiByte(CP_UTF8, 0, text, -1, NULL, 0, NULL, NULL); - std::string s( length-1, '\0' ); - WideCharToMultiByte(CP_UTF8, 0, text, -1, &s[0], length, NULL, NULL); - return s; - } - -#elif defined(__LINUX_ALSA__) || defined(__LINUX_PULSE__) || defined(__UNIX_JACK__) || defined(__LINUX_OSS__) || defined(__MACOSX_CORE__) - // pthread API - #define MUTEX_INITIALIZE(A) pthread_mutex_init(A, NULL) - #define MUTEX_DESTROY(A) pthread_mutex_destroy(A) - #define MUTEX_LOCK(A) pthread_mutex_lock(A) - #define MUTEX_UNLOCK(A) pthread_mutex_unlock(A) -#else - #define MUTEX_INITIALIZE(A) abs(*A) // dummy definitions - #define MUTEX_DESTROY(A) abs(*A) // dummy definitions -#endif - -// *************************************************** // -// -// RtAudio definitions. -// -// *************************************************** // - -std::string RtAudio :: getVersion( void ) -{ - return RTAUDIO_VERSION; -} - -// Define API names and display names. -// Must be in same order as API enum. -extern "C" { -const char* rtaudio_api_names[][2] = { - { "unspecified" , "Unknown" }, - { "alsa" , "ALSA" }, - { "pulse" , "Pulse" }, - { "oss" , "OpenSoundSystem" }, - { "jack" , "Jack" }, - { "core" , "CoreAudio" }, - { "wasapi" , "WASAPI" }, - { "asio" , "ASIO" }, - { "ds" , "DirectSound" }, - { "dummy" , "Dummy" }, -}; -const unsigned int rtaudio_num_api_names = - sizeof(rtaudio_api_names)/sizeof(rtaudio_api_names[0]); - -// The order here will control the order of RtAudio's API search in -// the constructor. -extern "C" const RtAudio::Api rtaudio_compiled_apis[] = { -#if defined(__UNIX_JACK__) - RtAudio::UNIX_JACK, -#endif -#if defined(__LINUX_PULSE__) - RtAudio::LINUX_PULSE, -#endif -#if defined(__LINUX_ALSA__) - RtAudio::LINUX_ALSA, -#endif -#if defined(__LINUX_OSS__) - RtAudio::LINUX_OSS, -#endif -#if defined(__WINDOWS_ASIO__) - RtAudio::WINDOWS_ASIO, -#endif -#if defined(__WINDOWS_WASAPI__) - RtAudio::WINDOWS_WASAPI, -#endif -#if defined(__WINDOWS_DS__) - RtAudio::WINDOWS_DS, -#endif -#if defined(__MACOSX_CORE__) - RtAudio::MACOSX_CORE, -#endif -#if defined(__RTAUDIO_DUMMY__) - RtAudio::RTAUDIO_DUMMY, -#endif - RtAudio::UNSPECIFIED, -}; -extern "C" const unsigned int rtaudio_num_compiled_apis = - sizeof(rtaudio_compiled_apis)/sizeof(rtaudio_compiled_apis[0])-1; -} - -// This is a compile-time check that rtaudio_num_api_names == RtAudio::NUM_APIS. -// If the build breaks here, check that they match. -template class StaticAssert { private: StaticAssert() {} }; -template<> class StaticAssert{ public: StaticAssert() {} }; -class StaticAssertions { StaticAssertions() { - StaticAssert(); -}}; - -void RtAudio :: getCompiledApi( std::vector &apis ) -{ - apis = std::vector(rtaudio_compiled_apis, - rtaudio_compiled_apis + rtaudio_num_compiled_apis); -} - -std::string RtAudio :: getApiName( RtAudio::Api api ) -{ - if (api < 0 || api >= RtAudio::NUM_APIS) - return ""; - return rtaudio_api_names[api][0]; -} - -std::string RtAudio :: getApiDisplayName( RtAudio::Api api ) -{ - if (api < 0 || api >= RtAudio::NUM_APIS) - return "Unknown"; - return rtaudio_api_names[api][1]; -} - -RtAudio::Api RtAudio :: getCompiledApiByName( const std::string &name ) -{ - unsigned int i=0; - for (i = 0; i < rtaudio_num_compiled_apis; ++i) - if (name == rtaudio_api_names[rtaudio_compiled_apis[i]][0]) - return rtaudio_compiled_apis[i]; - return RtAudio::UNSPECIFIED; -} - -void RtAudio :: openRtApi( RtAudio::Api api ) -{ - if ( rtapi_ ) - delete rtapi_; - rtapi_ = 0; - -#if defined(__UNIX_JACK__) - if ( api == UNIX_JACK ) - rtapi_ = new RtApiJack(); -#endif -#if defined(__LINUX_ALSA__) - if ( api == LINUX_ALSA ) - rtapi_ = new RtApiAlsa(); -#endif -#if defined(__LINUX_PULSE__) - if ( api == LINUX_PULSE ) - rtapi_ = new RtApiPulse(); -#endif -#if defined(__LINUX_OSS__) - if ( api == LINUX_OSS ) - rtapi_ = new RtApiOss(); -#endif -#if defined(__WINDOWS_ASIO__) - if ( api == WINDOWS_ASIO ) - rtapi_ = new RtApiAsio(); -#endif -#if defined(__WINDOWS_WASAPI__) - if ( api == WINDOWS_WASAPI ) - rtapi_ = new RtApiWasapi(); -#endif -#if defined(__WINDOWS_DS__) - if ( api == WINDOWS_DS ) - rtapi_ = new RtApiDs(); -#endif -#if defined(__MACOSX_CORE__) - if ( api == MACOSX_CORE ) - rtapi_ = new RtApiCore(); -#endif -#if defined(__RTAUDIO_DUMMY__) - if ( api == RTAUDIO_DUMMY ) - rtapi_ = new RtApiDummy(); -#endif -} - -RtAudio :: RtAudio( RtAudio::Api api ) -{ - rtapi_ = 0; - - if ( api != UNSPECIFIED ) { - // Attempt to open the specified API. - openRtApi( api ); - if ( rtapi_ ) return; - - // No compiled support for specified API value. Issue a debug - // warning and continue as if no API was specified. - std::cerr << "\nRtAudio: no compiled support for specified API argument!\n" << std::endl; - } - - // Iterate through the compiled APIs and return as soon as we find - // one with at least one device or we reach the end of the list. - std::vector< RtAudio::Api > apis; - getCompiledApi( apis ); - for ( unsigned int i=0; igetDeviceCount() ) break; - } - - if ( rtapi_ ) return; - - // It should not be possible to get here because the preprocessor - // definition __RTAUDIO_DUMMY__ is automatically defined if no - // API-specific definitions are passed to the compiler. But just in - // case something weird happens, we'll thow an error. - std::string errorText = "\nRtAudio: no compiled API support found ... critical error!!\n\n"; - throw( RtAudioError( errorText, RtAudioError::UNSPECIFIED ) ); -} - -RtAudio :: ~RtAudio() -{ - if ( rtapi_ ) - delete rtapi_; -} - -void RtAudio :: openStream( RtAudio::StreamParameters *outputParameters, - RtAudio::StreamParameters *inputParameters, - RtAudioFormat format, unsigned int sampleRate, - unsigned int *bufferFrames, - RtAudioCallback callback, void *userData, - RtAudio::StreamOptions *options, - RtAudioErrorCallback errorCallback ) -{ - return rtapi_->openStream( outputParameters, inputParameters, format, - sampleRate, bufferFrames, callback, - userData, options, errorCallback ); -} - -// *************************************************** // -// -// Public RtApi definitions (see end of file for -// private or protected utility functions). -// -// *************************************************** // - -RtApi :: RtApi() -{ - stream_.state = STREAM_CLOSED; - stream_.mode = UNINITIALIZED; - stream_.apiHandle = 0; - stream_.userBuffer[0] = 0; - stream_.userBuffer[1] = 0; - MUTEX_INITIALIZE( &stream_.mutex ); - showWarnings_ = true; - firstErrorOccurred_ = false; -} - -RtApi :: ~RtApi() -{ - MUTEX_DESTROY( &stream_.mutex ); -} - -void RtApi :: openStream( RtAudio::StreamParameters *oParams, - RtAudio::StreamParameters *iParams, - RtAudioFormat format, unsigned int sampleRate, - unsigned int *bufferFrames, - RtAudioCallback callback, void *userData, - RtAudio::StreamOptions *options, - RtAudioErrorCallback errorCallback ) -{ - if ( stream_.state != STREAM_CLOSED ) { - errorText_ = "RtApi::openStream: a stream is already open!"; - error( RtAudioError::INVALID_USE ); - return; - } - - // Clear stream information potentially left from a previously open stream. - clearStreamInfo(); - - if ( oParams && oParams->nChannels < 1 ) { - errorText_ = "RtApi::openStream: a non-NULL output StreamParameters structure cannot have an nChannels value less than one."; - error( RtAudioError::INVALID_USE ); - return; - } - - if ( iParams && iParams->nChannels < 1 ) { - errorText_ = "RtApi::openStream: a non-NULL input StreamParameters structure cannot have an nChannels value less than one."; - error( RtAudioError::INVALID_USE ); - return; - } - - if ( oParams == NULL && iParams == NULL ) { - errorText_ = "RtApi::openStream: input and output StreamParameters structures are both NULL!"; - error( RtAudioError::INVALID_USE ); - return; - } - - if ( formatBytes(format) == 0 ) { - errorText_ = "RtApi::openStream: 'format' parameter value is undefined."; - error( RtAudioError::INVALID_USE ); - return; - } - - unsigned int nDevices = getDeviceCount(); - unsigned int oChannels = 0; - if ( oParams ) { - oChannels = oParams->nChannels; - if ( oParams->deviceId >= nDevices ) { - errorText_ = "RtApi::openStream: output device parameter value is invalid."; - error( RtAudioError::INVALID_USE ); - return; - } - } - - unsigned int iChannels = 0; - if ( iParams ) { - iChannels = iParams->nChannels; - if ( iParams->deviceId >= nDevices ) { - errorText_ = "RtApi::openStream: input device parameter value is invalid."; - error( RtAudioError::INVALID_USE ); - return; - } - } - - bool result; - - if ( oChannels > 0 ) { - - result = probeDeviceOpen( oParams->deviceId, OUTPUT, oChannels, oParams->firstChannel, - sampleRate, format, bufferFrames, options ); - if ( result == false ) { - error( RtAudioError::SYSTEM_ERROR ); - return; - } - } - - if ( iChannels > 0 ) { - - result = probeDeviceOpen( iParams->deviceId, INPUT, iChannels, iParams->firstChannel, - sampleRate, format, bufferFrames, options ); - if ( result == false ) { - if ( oChannels > 0 ) closeStream(); - error( RtAudioError::SYSTEM_ERROR ); - return; - } - } - - stream_.callbackInfo.callback = (void *) callback; - stream_.callbackInfo.userData = userData; - stream_.callbackInfo.errorCallback = (void *) errorCallback; - - if ( options ) options->numberOfBuffers = stream_.nBuffers; - stream_.state = STREAM_STOPPED; -} - -unsigned int RtApi :: getDefaultInputDevice( void ) -{ - // Should be implemented in subclasses if possible. - return 0; -} - -unsigned int RtApi :: getDefaultOutputDevice( void ) -{ - // Should be implemented in subclasses if possible. - return 0; -} - -void RtApi :: closeStream( void ) -{ - // MUST be implemented in subclasses! - return; -} - -bool RtApi :: probeDeviceOpen( unsigned int /*device*/, StreamMode /*mode*/, unsigned int /*channels*/, - unsigned int /*firstChannel*/, unsigned int /*sampleRate*/, - RtAudioFormat /*format*/, unsigned int * /*bufferSize*/, - RtAudio::StreamOptions * /*options*/ ) -{ - // MUST be implemented in subclasses! - return FAILURE; -} - -void RtApi :: tickStreamTime( void ) -{ - // Subclasses that do not provide their own implementation of - // getStreamTime should call this function once per buffer I/O to - // provide basic stream time support. - - stream_.streamTime += ( stream_.bufferSize * 1.0 / stream_.sampleRate ); - -#if defined( HAVE_GETTIMEOFDAY ) - gettimeofday( &stream_.lastTickTimestamp, NULL ); -#endif -} - -long RtApi :: getStreamLatency( void ) -{ - verifyStream(); - - long totalLatency = 0; - if ( stream_.mode == OUTPUT || stream_.mode == DUPLEX ) - totalLatency = stream_.latency[0]; - if ( stream_.mode == INPUT || stream_.mode == DUPLEX ) - totalLatency += stream_.latency[1]; - - return totalLatency; -} - -double RtApi :: getStreamTime( void ) -{ - verifyStream(); - -#if defined( HAVE_GETTIMEOFDAY ) - // Return a very accurate estimate of the stream time by - // adding in the elapsed time since the last tick. - struct timeval then; - struct timeval now; - - if ( stream_.state != STREAM_RUNNING || stream_.streamTime == 0.0 ) - return stream_.streamTime; - - gettimeofday( &now, NULL ); - then = stream_.lastTickTimestamp; - return stream_.streamTime + - ((now.tv_sec + 0.000001 * now.tv_usec) - - (then.tv_sec + 0.000001 * then.tv_usec)); -#else - return stream_.streamTime; -#endif -} - -void RtApi :: setStreamTime( double time ) -{ - verifyStream(); - - if ( time >= 0.0 ) - stream_.streamTime = time; -#if defined( HAVE_GETTIMEOFDAY ) - gettimeofday( &stream_.lastTickTimestamp, NULL ); -#endif -} - -unsigned int RtApi :: getStreamSampleRate( void ) -{ - verifyStream(); - - return stream_.sampleRate; -} - - -// *************************************************** // -// -// OS/API-specific methods. -// -// *************************************************** // - -#if defined(__MACOSX_CORE__) - -// The OS X CoreAudio API is designed to use a separate callback -// procedure for each of its audio devices. A single RtAudio duplex -// stream using two different devices is supported here, though it -// cannot be guaranteed to always behave correctly because we cannot -// synchronize these two callbacks. -// -// A property listener is installed for over/underrun information. -// However, no functionality is currently provided to allow property -// listeners to trigger user handlers because it is unclear what could -// be done if a critical stream parameter (buffer size, sample rate, -// device disconnect) notification arrived. The listeners entail -// quite a bit of extra code and most likely, a user program wouldn't -// be prepared for the result anyway. However, we do provide a flag -// to the client callback function to inform of an over/underrun. - -// A structure to hold various information related to the CoreAudio API -// implementation. -struct CoreHandle { - AudioDeviceID id[2]; // device ids -#if defined( MAC_OS_X_VERSION_10_5 ) && ( MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_5 ) - AudioDeviceIOProcID procId[2]; -#endif - UInt32 iStream[2]; // device stream index (or first if using multiple) - UInt32 nStreams[2]; // number of streams to use - bool xrun[2]; - char *deviceBuffer; - pthread_cond_t condition; - int drainCounter; // Tracks callback counts when draining - bool internalDrain; // Indicates if stop is initiated from callback or not. - - CoreHandle() - :deviceBuffer(0), drainCounter(0), internalDrain(false) { nStreams[0] = 1; nStreams[1] = 1; id[0] = 0; id[1] = 0; xrun[0] = false; xrun[1] = false; } -}; - -RtApiCore:: RtApiCore() -{ -#if defined( AVAILABLE_MAC_OS_X_VERSION_10_6_AND_LATER ) - // This is a largely undocumented but absolutely necessary - // requirement starting with OS-X 10.6. If not called, queries and - // updates to various audio device properties are not handled - // correctly. - CFRunLoopRef theRunLoop = NULL; - AudioObjectPropertyAddress property = { kAudioHardwarePropertyRunLoop, - kAudioObjectPropertyScopeGlobal, - kAudioObjectPropertyElementMaster }; - OSStatus result = AudioObjectSetPropertyData( kAudioObjectSystemObject, &property, 0, NULL, sizeof(CFRunLoopRef), &theRunLoop); - if ( result != noErr ) { - errorText_ = "RtApiCore::RtApiCore: error setting run loop property!"; - error( RtAudioError::WARNING ); - } -#endif -} - -RtApiCore :: ~RtApiCore() -{ - // The subclass destructor gets called before the base class - // destructor, so close an existing stream before deallocating - // apiDeviceId memory. - if ( stream_.state != STREAM_CLOSED ) closeStream(); -} - -unsigned int RtApiCore :: getDeviceCount( void ) -{ - // Find out how many audio devices there are, if any. - UInt32 dataSize; - AudioObjectPropertyAddress propertyAddress = { kAudioHardwarePropertyDevices, kAudioObjectPropertyScopeGlobal, kAudioObjectPropertyElementMaster }; - OSStatus result = AudioObjectGetPropertyDataSize( kAudioObjectSystemObject, &propertyAddress, 0, NULL, &dataSize ); - if ( result != noErr ) { - errorText_ = "RtApiCore::getDeviceCount: OS-X error getting device info!"; - error( RtAudioError::WARNING ); - return 0; - } - - return dataSize / sizeof( AudioDeviceID ); -} - -unsigned int RtApiCore :: getDefaultInputDevice( void ) -{ - unsigned int nDevices = getDeviceCount(); - if ( nDevices <= 1 ) return 0; - - AudioDeviceID id; - UInt32 dataSize = sizeof( AudioDeviceID ); - AudioObjectPropertyAddress property = { kAudioHardwarePropertyDefaultInputDevice, kAudioObjectPropertyScopeGlobal, kAudioObjectPropertyElementMaster }; - OSStatus result = AudioObjectGetPropertyData( kAudioObjectSystemObject, &property, 0, NULL, &dataSize, &id ); - if ( result != noErr ) { - errorText_ = "RtApiCore::getDefaultInputDevice: OS-X system error getting device."; - error( RtAudioError::WARNING ); - return 0; - } - - dataSize *= nDevices; - AudioDeviceID deviceList[ nDevices ]; - property.mSelector = kAudioHardwarePropertyDevices; - result = AudioObjectGetPropertyData( kAudioObjectSystemObject, &property, 0, NULL, &dataSize, (void *) &deviceList ); - if ( result != noErr ) { - errorText_ = "RtApiCore::getDefaultInputDevice: OS-X system error getting device IDs."; - error( RtAudioError::WARNING ); - return 0; - } - - for ( unsigned int i=0; i= nDevices ) { - errorText_ = "RtApiCore::getDeviceInfo: device ID is invalid!"; - error( RtAudioError::INVALID_USE ); - return info; - } - - AudioDeviceID deviceList[ nDevices ]; - UInt32 dataSize = sizeof( AudioDeviceID ) * nDevices; - AudioObjectPropertyAddress property = { kAudioHardwarePropertyDevices, - kAudioObjectPropertyScopeGlobal, - kAudioObjectPropertyElementMaster }; - OSStatus result = AudioObjectGetPropertyData( kAudioObjectSystemObject, &property, - 0, NULL, &dataSize, (void *) &deviceList ); - if ( result != noErr ) { - errorText_ = "RtApiCore::getDeviceInfo: OS-X system error getting device IDs."; - error( RtAudioError::WARNING ); - return info; - } - - AudioDeviceID id = deviceList[ device ]; - - // Get the device name. - info.name.erase(); - CFStringRef cfname; - dataSize = sizeof( CFStringRef ); - property.mSelector = kAudioObjectPropertyManufacturer; - result = AudioObjectGetPropertyData( id, &property, 0, NULL, &dataSize, &cfname ); - if ( result != noErr ) { - errorStream_ << "RtApiCore::probeDeviceInfo: system error (" << getErrorCode( result ) << ") getting device manufacturer."; - errorText_ = errorStream_.str(); - error( RtAudioError::WARNING ); - return info; - } - - //const char *mname = CFStringGetCStringPtr( cfname, CFStringGetSystemEncoding() ); - int length = CFStringGetLength(cfname); - char *mname = (char *)malloc(length * 3 + 1); -#if defined( UNICODE ) || defined( _UNICODE ) - CFStringGetCString(cfname, mname, length * 3 + 1, kCFStringEncodingUTF8); -#else - CFStringGetCString(cfname, mname, length * 3 + 1, CFStringGetSystemEncoding()); -#endif - info.name.append( (const char *)mname, strlen(mname) ); - info.name.append( ": " ); - CFRelease( cfname ); - free(mname); - - property.mSelector = kAudioObjectPropertyName; - result = AudioObjectGetPropertyData( id, &property, 0, NULL, &dataSize, &cfname ); - if ( result != noErr ) { - errorStream_ << "RtApiCore::probeDeviceInfo: system error (" << getErrorCode( result ) << ") getting device name."; - errorText_ = errorStream_.str(); - error( RtAudioError::WARNING ); - return info; - } - - //const char *name = CFStringGetCStringPtr( cfname, CFStringGetSystemEncoding() ); - length = CFStringGetLength(cfname); - char *name = (char *)malloc(length * 3 + 1); -#if defined( UNICODE ) || defined( _UNICODE ) - CFStringGetCString(cfname, name, length * 3 + 1, kCFStringEncodingUTF8); -#else - CFStringGetCString(cfname, name, length * 3 + 1, CFStringGetSystemEncoding()); -#endif - info.name.append( (const char *)name, strlen(name) ); - CFRelease( cfname ); - free(name); - - // Get the output stream "configuration". - AudioBufferList *bufferList = nil; - property.mSelector = kAudioDevicePropertyStreamConfiguration; - property.mScope = kAudioDevicePropertyScopeOutput; - // property.mElement = kAudioObjectPropertyElementWildcard; - dataSize = 0; - result = AudioObjectGetPropertyDataSize( id, &property, 0, NULL, &dataSize ); - if ( result != noErr || dataSize == 0 ) { - errorStream_ << "RtApiCore::getDeviceInfo: system error (" << getErrorCode( result ) << ") getting output stream configuration info for device (" << device << ")."; - errorText_ = errorStream_.str(); - error( RtAudioError::WARNING ); - return info; - } - - // Allocate the AudioBufferList. - bufferList = (AudioBufferList *) malloc( dataSize ); - if ( bufferList == NULL ) { - errorText_ = "RtApiCore::getDeviceInfo: memory error allocating output AudioBufferList."; - error( RtAudioError::WARNING ); - return info; - } - - result = AudioObjectGetPropertyData( id, &property, 0, NULL, &dataSize, bufferList ); - if ( result != noErr || dataSize == 0 ) { - free( bufferList ); - errorStream_ << "RtApiCore::getDeviceInfo: system error (" << getErrorCode( result ) << ") getting output stream configuration for device (" << device << ")."; - errorText_ = errorStream_.str(); - error( RtAudioError::WARNING ); - return info; - } - - // Get output channel information. - unsigned int i, nStreams = bufferList->mNumberBuffers; - for ( i=0; imBuffers[i].mNumberChannels; - free( bufferList ); - - // Get the input stream "configuration". - property.mScope = kAudioDevicePropertyScopeInput; - result = AudioObjectGetPropertyDataSize( id, &property, 0, NULL, &dataSize ); - if ( result != noErr || dataSize == 0 ) { - errorStream_ << "RtApiCore::getDeviceInfo: system error (" << getErrorCode( result ) << ") getting input stream configuration info for device (" << device << ")."; - errorText_ = errorStream_.str(); - error( RtAudioError::WARNING ); - return info; - } - - // Allocate the AudioBufferList. - bufferList = (AudioBufferList *) malloc( dataSize ); - if ( bufferList == NULL ) { - errorText_ = "RtApiCore::getDeviceInfo: memory error allocating input AudioBufferList."; - error( RtAudioError::WARNING ); - return info; - } - - result = AudioObjectGetPropertyData( id, &property, 0, NULL, &dataSize, bufferList ); - if (result != noErr || dataSize == 0) { - free( bufferList ); - errorStream_ << "RtApiCore::getDeviceInfo: system error (" << getErrorCode( result ) << ") getting input stream configuration for device (" << device << ")."; - errorText_ = errorStream_.str(); - error( RtAudioError::WARNING ); - return info; - } - - // Get input channel information. - nStreams = bufferList->mNumberBuffers; - for ( i=0; imBuffers[i].mNumberChannels; - free( bufferList ); - - // If device opens for both playback and capture, we determine the channels. - if ( info.outputChannels > 0 && info.inputChannels > 0 ) - info.duplexChannels = (info.outputChannels > info.inputChannels) ? info.inputChannels : info.outputChannels; - - // Probe the device sample rates. - bool isInput = false; - if ( info.outputChannels == 0 ) isInput = true; - - // Determine the supported sample rates. - property.mSelector = kAudioDevicePropertyAvailableNominalSampleRates; - if ( isInput == false ) property.mScope = kAudioDevicePropertyScopeOutput; - result = AudioObjectGetPropertyDataSize( id, &property, 0, NULL, &dataSize ); - if ( result != kAudioHardwareNoError || dataSize == 0 ) { - errorStream_ << "RtApiCore::getDeviceInfo: system error (" << getErrorCode( result ) << ") getting sample rate info."; - errorText_ = errorStream_.str(); - error( RtAudioError::WARNING ); - return info; - } - - UInt32 nRanges = dataSize / sizeof( AudioValueRange ); - AudioValueRange rangeList[ nRanges ]; - result = AudioObjectGetPropertyData( id, &property, 0, NULL, &dataSize, &rangeList ); - if ( result != kAudioHardwareNoError ) { - errorStream_ << "RtApiCore::getDeviceInfo: system error (" << getErrorCode( result ) << ") getting sample rates."; - errorText_ = errorStream_.str(); - error( RtAudioError::WARNING ); - return info; - } - - // The sample rate reporting mechanism is a bit of a mystery. It - // seems that it can either return individual rates or a range of - // rates. I assume that if the min / max range values are the same, - // then that represents a single supported rate and if the min / max - // range values are different, the device supports an arbitrary - // range of values (though there might be multiple ranges, so we'll - // use the most conservative range). - Float64 minimumRate = 1.0, maximumRate = 10000000000.0; - bool haveValueRange = false; - info.sampleRates.clear(); - for ( UInt32 i=0; i info.preferredSampleRate ) ) - info.preferredSampleRate = tmpSr; - - } else { - haveValueRange = true; - if ( rangeList[i].mMinimum > minimumRate ) minimumRate = rangeList[i].mMinimum; - if ( rangeList[i].mMaximum < maximumRate ) maximumRate = rangeList[i].mMaximum; - } - } - - if ( haveValueRange ) { - for ( unsigned int k=0; k= (unsigned int) minimumRate && SAMPLE_RATES[k] <= (unsigned int) maximumRate ) { - info.sampleRates.push_back( SAMPLE_RATES[k] ); - - if ( !info.preferredSampleRate || ( SAMPLE_RATES[k] <= 48000 && SAMPLE_RATES[k] > info.preferredSampleRate ) ) - info.preferredSampleRate = SAMPLE_RATES[k]; - } - } - } - - // Sort and remove any redundant values - std::sort( info.sampleRates.begin(), info.sampleRates.end() ); - info.sampleRates.erase( unique( info.sampleRates.begin(), info.sampleRates.end() ), info.sampleRates.end() ); - - if ( info.sampleRates.size() == 0 ) { - errorStream_ << "RtApiCore::probeDeviceInfo: No supported sample rates found for device (" << device << ")."; - errorText_ = errorStream_.str(); - error( RtAudioError::WARNING ); - return info; - } - - // CoreAudio always uses 32-bit floating point data for PCM streams. - // Thus, any other "physical" formats supported by the device are of - // no interest to the client. - info.nativeFormats = RTAUDIO_FLOAT32; - - if ( info.outputChannels > 0 ) - if ( getDefaultOutputDevice() == device ) info.isDefaultOutput = true; - if ( info.inputChannels > 0 ) - if ( getDefaultInputDevice() == device ) info.isDefaultInput = true; - - info.probed = true; - return info; -} - -static OSStatus callbackHandler( AudioDeviceID inDevice, - const AudioTimeStamp* /*inNow*/, - const AudioBufferList* inInputData, - const AudioTimeStamp* /*inInputTime*/, - AudioBufferList* outOutputData, - const AudioTimeStamp* /*inOutputTime*/, - void* infoPointer ) -{ - CallbackInfo *info = (CallbackInfo *) infoPointer; - - RtApiCore *object = (RtApiCore *) info->object; - if ( object->callbackEvent( inDevice, inInputData, outOutputData ) == false ) - return kAudioHardwareUnspecifiedError; - else - return kAudioHardwareNoError; -} - -static OSStatus xrunListener( AudioObjectID /*inDevice*/, - UInt32 nAddresses, - const AudioObjectPropertyAddress properties[], - void* handlePointer ) -{ - CoreHandle *handle = (CoreHandle *) handlePointer; - for ( UInt32 i=0; ixrun[1] = true; - else - handle->xrun[0] = true; - } - } - - return kAudioHardwareNoError; -} - -static OSStatus rateListener( AudioObjectID inDevice, - UInt32 /*nAddresses*/, - const AudioObjectPropertyAddress /*properties*/[], - void* ratePointer ) -{ - Float64 *rate = (Float64 *) ratePointer; - UInt32 dataSize = sizeof( Float64 ); - AudioObjectPropertyAddress property = { kAudioDevicePropertyNominalSampleRate, - kAudioObjectPropertyScopeGlobal, - kAudioObjectPropertyElementMaster }; - AudioObjectGetPropertyData( inDevice, &property, 0, NULL, &dataSize, rate ); - return kAudioHardwareNoError; -} - -bool RtApiCore :: probeDeviceOpen( unsigned int device, StreamMode mode, unsigned int channels, - unsigned int firstChannel, unsigned int sampleRate, - RtAudioFormat format, unsigned int *bufferSize, - RtAudio::StreamOptions *options ) -{ - // Get device ID - unsigned int nDevices = getDeviceCount(); - if ( nDevices == 0 ) { - // This should not happen because a check is made before this function is called. - errorText_ = "RtApiCore::probeDeviceOpen: no devices found!"; - return FAILURE; - } - - if ( device >= nDevices ) { - // This should not happen because a check is made before this function is called. - errorText_ = "RtApiCore::probeDeviceOpen: device ID is invalid!"; - return FAILURE; - } - - AudioDeviceID deviceList[ nDevices ]; - UInt32 dataSize = sizeof( AudioDeviceID ) * nDevices; - AudioObjectPropertyAddress property = { kAudioHardwarePropertyDevices, - kAudioObjectPropertyScopeGlobal, - kAudioObjectPropertyElementMaster }; - OSStatus result = AudioObjectGetPropertyData( kAudioObjectSystemObject, &property, - 0, NULL, &dataSize, (void *) &deviceList ); - if ( result != noErr ) { - errorText_ = "RtApiCore::probeDeviceOpen: OS-X system error getting device IDs."; - return FAILURE; - } - - AudioDeviceID id = deviceList[ device ]; - - // Setup for stream mode. - bool isInput = false; - if ( mode == INPUT ) { - isInput = true; - property.mScope = kAudioDevicePropertyScopeInput; - } - else - property.mScope = kAudioDevicePropertyScopeOutput; - - // Get the stream "configuration". - AudioBufferList *bufferList = nil; - dataSize = 0; - property.mSelector = kAudioDevicePropertyStreamConfiguration; - result = AudioObjectGetPropertyDataSize( id, &property, 0, NULL, &dataSize ); - if ( result != noErr || dataSize == 0 ) { - errorStream_ << "RtApiCore::probeDeviceOpen: system error (" << getErrorCode( result ) << ") getting stream configuration info for device (" << device << ")."; - errorText_ = errorStream_.str(); - return FAILURE; - } - - // Allocate the AudioBufferList. - bufferList = (AudioBufferList *) malloc( dataSize ); - if ( bufferList == NULL ) { - errorText_ = "RtApiCore::probeDeviceOpen: memory error allocating AudioBufferList."; - return FAILURE; - } - - result = AudioObjectGetPropertyData( id, &property, 0, NULL, &dataSize, bufferList ); - if (result != noErr || dataSize == 0) { - free( bufferList ); - errorStream_ << "RtApiCore::probeDeviceOpen: system error (" << getErrorCode( result ) << ") getting stream configuration for device (" << device << ")."; - errorText_ = errorStream_.str(); - return FAILURE; - } - - // Search for one or more streams that contain the desired number of - // channels. CoreAudio devices can have an arbitrary number of - // streams and each stream can have an arbitrary number of channels. - // For each stream, a single buffer of interleaved samples is - // provided. RtAudio prefers the use of one stream of interleaved - // data or multiple consecutive single-channel streams. However, we - // now support multiple consecutive multi-channel streams of - // interleaved data as well. - UInt32 iStream, offsetCounter = firstChannel; - UInt32 nStreams = bufferList->mNumberBuffers; - bool monoMode = false; - bool foundStream = false; - - // First check that the device supports the requested number of - // channels. - UInt32 deviceChannels = 0; - for ( iStream=0; iStreammBuffers[iStream].mNumberChannels; - - if ( deviceChannels < ( channels + firstChannel ) ) { - free( bufferList ); - errorStream_ << "RtApiCore::probeDeviceOpen: the device (" << device << ") does not support the requested channel count."; - errorText_ = errorStream_.str(); - return FAILURE; - } - - // Look for a single stream meeting our needs. - UInt32 firstStream, streamCount = 1, streamChannels = 0, channelOffset = 0; - for ( iStream=0; iStreammBuffers[iStream].mNumberChannels; - if ( streamChannels >= channels + offsetCounter ) { - firstStream = iStream; - channelOffset = offsetCounter; - foundStream = true; - break; - } - if ( streamChannels > offsetCounter ) break; - offsetCounter -= streamChannels; - } - - // If we didn't find a single stream above, then we should be able - // to meet the channel specification with multiple streams. - if ( foundStream == false ) { - monoMode = true; - offsetCounter = firstChannel; - for ( iStream=0; iStreammBuffers[iStream].mNumberChannels; - if ( streamChannels > offsetCounter ) break; - offsetCounter -= streamChannels; - } - - firstStream = iStream; - channelOffset = offsetCounter; - Int32 channelCounter = channels + offsetCounter - streamChannels; - - if ( streamChannels > 1 ) monoMode = false; - while ( channelCounter > 0 ) { - streamChannels = bufferList->mBuffers[++iStream].mNumberChannels; - if ( streamChannels > 1 ) monoMode = false; - channelCounter -= streamChannels; - streamCount++; - } - } - - free( bufferList ); - - // Determine the buffer size. - AudioValueRange bufferRange; - dataSize = sizeof( AudioValueRange ); - property.mSelector = kAudioDevicePropertyBufferFrameSizeRange; - result = AudioObjectGetPropertyData( id, &property, 0, NULL, &dataSize, &bufferRange ); - - if ( result != noErr ) { - errorStream_ << "RtApiCore::probeDeviceOpen: system error (" << getErrorCode( result ) << ") getting buffer size range for device (" << device << ")."; - errorText_ = errorStream_.str(); - return FAILURE; - } - - if ( bufferRange.mMinimum > *bufferSize ) *bufferSize = (unsigned long) bufferRange.mMinimum; - else if ( bufferRange.mMaximum < *bufferSize ) *bufferSize = (unsigned long) bufferRange.mMaximum; - if ( options && options->flags & RTAUDIO_MINIMIZE_LATENCY ) *bufferSize = (unsigned long) bufferRange.mMinimum; - - // Set the buffer size. For multiple streams, I'm assuming we only - // need to make this setting for the master channel. - UInt32 theSize = (UInt32) *bufferSize; - dataSize = sizeof( UInt32 ); - property.mSelector = kAudioDevicePropertyBufferFrameSize; - result = AudioObjectSetPropertyData( id, &property, 0, NULL, dataSize, &theSize ); - - if ( result != noErr ) { - errorStream_ << "RtApiCore::probeDeviceOpen: system error (" << getErrorCode( result ) << ") setting the buffer size for device (" << device << ")."; - errorText_ = errorStream_.str(); - return FAILURE; - } - - // If attempting to setup a duplex stream, the bufferSize parameter - // MUST be the same in both directions! - *bufferSize = theSize; - if ( stream_.mode == OUTPUT && mode == INPUT && *bufferSize != stream_.bufferSize ) { - errorStream_ << "RtApiCore::probeDeviceOpen: system error setting buffer size for duplex stream on device (" << device << ")."; - errorText_ = errorStream_.str(); - return FAILURE; - } - - stream_.bufferSize = *bufferSize; - stream_.nBuffers = 1; - - // Try to set "hog" mode ... it's not clear to me this is working. - if ( options && options->flags & RTAUDIO_HOG_DEVICE ) { - pid_t hog_pid; - dataSize = sizeof( hog_pid ); - property.mSelector = kAudioDevicePropertyHogMode; - result = AudioObjectGetPropertyData( id, &property, 0, NULL, &dataSize, &hog_pid ); - if ( result != noErr ) { - errorStream_ << "RtApiCore::probeDeviceOpen: system error (" << getErrorCode( result ) << ") getting 'hog' state!"; - errorText_ = errorStream_.str(); - return FAILURE; - } - - if ( hog_pid != getpid() ) { - hog_pid = getpid(); - result = AudioObjectSetPropertyData( id, &property, 0, NULL, dataSize, &hog_pid ); - if ( result != noErr ) { - errorStream_ << "RtApiCore::probeDeviceOpen: system error (" << getErrorCode( result ) << ") setting 'hog' state!"; - errorText_ = errorStream_.str(); - return FAILURE; - } - } - } - - // Check and if necessary, change the sample rate for the device. - Float64 nominalRate; - dataSize = sizeof( Float64 ); - property.mSelector = kAudioDevicePropertyNominalSampleRate; - result = AudioObjectGetPropertyData( id, &property, 0, NULL, &dataSize, &nominalRate ); - if ( result != noErr ) { - errorStream_ << "RtApiCore::probeDeviceOpen: system error (" << getErrorCode( result ) << ") getting current sample rate."; - errorText_ = errorStream_.str(); - return FAILURE; - } - - // Only change the sample rate if off by more than 1 Hz. - if ( fabs( nominalRate - (double)sampleRate ) > 1.0 ) { - - // Set a property listener for the sample rate change - Float64 reportedRate = 0.0; - AudioObjectPropertyAddress tmp = { kAudioDevicePropertyNominalSampleRate, kAudioObjectPropertyScopeGlobal, kAudioObjectPropertyElementMaster }; - result = AudioObjectAddPropertyListener( id, &tmp, rateListener, (void *) &reportedRate ); - if ( result != noErr ) { - errorStream_ << "RtApiCore::probeDeviceOpen: system error (" << getErrorCode( result ) << ") setting sample rate property listener for device (" << device << ")."; - errorText_ = errorStream_.str(); - return FAILURE; - } - - nominalRate = (Float64) sampleRate; - result = AudioObjectSetPropertyData( id, &property, 0, NULL, dataSize, &nominalRate ); - if ( result != noErr ) { - AudioObjectRemovePropertyListener( id, &tmp, rateListener, (void *) &reportedRate ); - errorStream_ << "RtApiCore::probeDeviceOpen: system error (" << getErrorCode( result ) << ") setting sample rate for device (" << device << ")."; - errorText_ = errorStream_.str(); - return FAILURE; - } - - // Now wait until the reported nominal rate is what we just set. - UInt32 microCounter = 0; - while ( reportedRate != nominalRate ) { - microCounter += 5000; - if ( microCounter > 5000000 ) break; - usleep( 5000 ); - } - - // Remove the property listener. - AudioObjectRemovePropertyListener( id, &tmp, rateListener, (void *) &reportedRate ); - - if ( microCounter > 5000000 ) { - errorStream_ << "RtApiCore::probeDeviceOpen: timeout waiting for sample rate update for device (" << device << ")."; - errorText_ = errorStream_.str(); - return FAILURE; - } - } - - // Now set the stream format for all streams. Also, check the - // physical format of the device and change that if necessary. - AudioStreamBasicDescription description; - dataSize = sizeof( AudioStreamBasicDescription ); - property.mSelector = kAudioStreamPropertyVirtualFormat; - result = AudioObjectGetPropertyData( id, &property, 0, NULL, &dataSize, &description ); - if ( result != noErr ) { - errorStream_ << "RtApiCore::probeDeviceOpen: system error (" << getErrorCode( result ) << ") getting stream format for device (" << device << ")."; - errorText_ = errorStream_.str(); - return FAILURE; - } - - // Set the sample rate and data format id. However, only make the - // change if the sample rate is not within 1.0 of the desired - // rate and the format is not linear pcm. - bool updateFormat = false; - if ( fabs( description.mSampleRate - (Float64)sampleRate ) > 1.0 ) { - description.mSampleRate = (Float64) sampleRate; - updateFormat = true; - } - - if ( description.mFormatID != kAudioFormatLinearPCM ) { - description.mFormatID = kAudioFormatLinearPCM; - updateFormat = true; - } - - if ( updateFormat ) { - result = AudioObjectSetPropertyData( id, &property, 0, NULL, dataSize, &description ); - if ( result != noErr ) { - errorStream_ << "RtApiCore::probeDeviceOpen: system error (" << getErrorCode( result ) << ") setting sample rate or data format for device (" << device << ")."; - errorText_ = errorStream_.str(); - return FAILURE; - } - } - - // Now check the physical format. - property.mSelector = kAudioStreamPropertyPhysicalFormat; - result = AudioObjectGetPropertyData( id, &property, 0, NULL, &dataSize, &description ); - if ( result != noErr ) { - errorStream_ << "RtApiCore::probeDeviceOpen: system error (" << getErrorCode( result ) << ") getting stream physical format for device (" << device << ")."; - errorText_ = errorStream_.str(); - return FAILURE; - } - - //std::cout << "Current physical stream format:" << std::endl; - //std::cout << " mBitsPerChan = " << description.mBitsPerChannel << std::endl; - //std::cout << " aligned high = " << (description.mFormatFlags & kAudioFormatFlagIsAlignedHigh) << ", isPacked = " << (description.mFormatFlags & kAudioFormatFlagIsPacked) << std::endl; - //std::cout << " bytesPerFrame = " << description.mBytesPerFrame << std::endl; - //std::cout << " sample rate = " << description.mSampleRate << std::endl; - - if ( description.mFormatID != kAudioFormatLinearPCM || description.mBitsPerChannel < 16 ) { - description.mFormatID = kAudioFormatLinearPCM; - //description.mSampleRate = (Float64) sampleRate; - AudioStreamBasicDescription testDescription = description; - UInt32 formatFlags; - - // We'll try higher bit rates first and then work our way down. - std::vector< std::pair > physicalFormats; - formatFlags = (description.mFormatFlags | kLinearPCMFormatFlagIsFloat) & ~kLinearPCMFormatFlagIsSignedInteger; - physicalFormats.push_back( std::pair( 32, formatFlags ) ); - formatFlags = (description.mFormatFlags | kLinearPCMFormatFlagIsSignedInteger | kAudioFormatFlagIsPacked) & ~kLinearPCMFormatFlagIsFloat; - physicalFormats.push_back( std::pair( 32, formatFlags ) ); - physicalFormats.push_back( std::pair( 24, formatFlags ) ); // 24-bit packed - formatFlags &= ~( kAudioFormatFlagIsPacked | kAudioFormatFlagIsAlignedHigh ); - physicalFormats.push_back( std::pair( 24.2, formatFlags ) ); // 24-bit in 4 bytes, aligned low - formatFlags |= kAudioFormatFlagIsAlignedHigh; - physicalFormats.push_back( std::pair( 24.4, formatFlags ) ); // 24-bit in 4 bytes, aligned high - formatFlags = (description.mFormatFlags | kLinearPCMFormatFlagIsSignedInteger | kAudioFormatFlagIsPacked) & ~kLinearPCMFormatFlagIsFloat; - physicalFormats.push_back( std::pair( 16, formatFlags ) ); - physicalFormats.push_back( std::pair( 8, formatFlags ) ); - - bool setPhysicalFormat = false; - for( unsigned int i=0; iflags & RTAUDIO_NONINTERLEAVED ) stream_.userInterleaved = false; - else stream_.userInterleaved = true; - stream_.deviceInterleaved[mode] = true; - if ( monoMode == true ) stream_.deviceInterleaved[mode] = false; - - // Set flags for buffer conversion. - stream_.doConvertBuffer[mode] = false; - if ( stream_.userFormat != stream_.deviceFormat[mode] ) - stream_.doConvertBuffer[mode] = true; - if ( stream_.nUserChannels[mode] < stream_.nDeviceChannels[mode] ) - stream_.doConvertBuffer[mode] = true; - if ( streamCount == 1 ) { - if ( stream_.nUserChannels[mode] > 1 && - stream_.userInterleaved != stream_.deviceInterleaved[mode] ) - stream_.doConvertBuffer[mode] = true; - } - else if ( monoMode && stream_.userInterleaved ) - stream_.doConvertBuffer[mode] = true; - - // Allocate our CoreHandle structure for the stream. - CoreHandle *handle = 0; - if ( stream_.apiHandle == 0 ) { - try { - handle = new CoreHandle; - } - catch ( std::bad_alloc& ) { - errorText_ = "RtApiCore::probeDeviceOpen: error allocating CoreHandle memory."; - goto error; - } - - if ( pthread_cond_init( &handle->condition, NULL ) ) { - errorText_ = "RtApiCore::probeDeviceOpen: error initializing pthread condition variable."; - goto error; - } - stream_.apiHandle = (void *) handle; - } - else - handle = (CoreHandle *) stream_.apiHandle; - handle->iStream[mode] = firstStream; - handle->nStreams[mode] = streamCount; - handle->id[mode] = id; - - // Allocate necessary internal buffers. - unsigned long bufferBytes; - bufferBytes = stream_.nUserChannels[mode] * *bufferSize * formatBytes( stream_.userFormat ); - // stream_.userBuffer[mode] = (char *) calloc( bufferBytes, 1 ); - stream_.userBuffer[mode] = (char *) malloc( bufferBytes * sizeof(char) ); - memset( stream_.userBuffer[mode], 0, bufferBytes * sizeof(char) ); - if ( stream_.userBuffer[mode] == NULL ) { - errorText_ = "RtApiCore::probeDeviceOpen: error allocating user buffer memory."; - goto error; - } - - // If possible, we will make use of the CoreAudio stream buffers as - // "device buffers". However, we can't do this if using multiple - // streams. - if ( stream_.doConvertBuffer[mode] && handle->nStreams[mode] > 1 ) { - - bool makeBuffer = true; - bufferBytes = stream_.nDeviceChannels[mode] * formatBytes( stream_.deviceFormat[mode] ); - if ( mode == INPUT ) { - if ( stream_.mode == OUTPUT && stream_.deviceBuffer ) { - unsigned long bytesOut = stream_.nDeviceChannels[0] * formatBytes( stream_.deviceFormat[0] ); - if ( bufferBytes <= bytesOut ) makeBuffer = false; - } - } - - if ( makeBuffer ) { - bufferBytes *= *bufferSize; - if ( stream_.deviceBuffer ) free( stream_.deviceBuffer ); - stream_.deviceBuffer = (char *) calloc( bufferBytes, 1 ); - if ( stream_.deviceBuffer == NULL ) { - errorText_ = "RtApiCore::probeDeviceOpen: error allocating device buffer memory."; - goto error; - } - } - } - - stream_.sampleRate = sampleRate; - stream_.device[mode] = device; - stream_.state = STREAM_STOPPED; - stream_.callbackInfo.object = (void *) this; - - // Setup the buffer conversion information structure. - if ( stream_.doConvertBuffer[mode] ) { - if ( streamCount > 1 ) setConvertInfo( mode, 0 ); - else setConvertInfo( mode, channelOffset ); - } - - if ( mode == INPUT && stream_.mode == OUTPUT && stream_.device[0] == device ) - // Only one callback procedure per device. - stream_.mode = DUPLEX; - else { -#if defined( MAC_OS_X_VERSION_10_5 ) && ( MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_5 ) - result = AudioDeviceCreateIOProcID( id, callbackHandler, (void *) &stream_.callbackInfo, &handle->procId[mode] ); -#else - // deprecated in favor of AudioDeviceCreateIOProcID() - result = AudioDeviceAddIOProc( id, callbackHandler, (void *) &stream_.callbackInfo ); -#endif - if ( result != noErr ) { - errorStream_ << "RtApiCore::probeDeviceOpen: system error setting callback for device (" << device << ")."; - errorText_ = errorStream_.str(); - goto error; - } - if ( stream_.mode == OUTPUT && mode == INPUT ) - stream_.mode = DUPLEX; - else - stream_.mode = mode; - } - - // Setup the device property listener for over/underload. - property.mSelector = kAudioDeviceProcessorOverload; - property.mScope = kAudioObjectPropertyScopeGlobal; - result = AudioObjectAddPropertyListener( id, &property, xrunListener, (void *) handle ); - - return SUCCESS; - - error: - if ( handle ) { - pthread_cond_destroy( &handle->condition ); - delete handle; - stream_.apiHandle = 0; - } - - for ( int i=0; i<2; i++ ) { - if ( stream_.userBuffer[i] ) { - free( stream_.userBuffer[i] ); - stream_.userBuffer[i] = 0; - } - } - - if ( stream_.deviceBuffer ) { - free( stream_.deviceBuffer ); - stream_.deviceBuffer = 0; - } - - stream_.state = STREAM_CLOSED; - return FAILURE; -} - -void RtApiCore :: closeStream( void ) -{ - if ( stream_.state == STREAM_CLOSED ) { - errorText_ = "RtApiCore::closeStream(): no open stream to close!"; - error( RtAudioError::WARNING ); - return; - } - - CoreHandle *handle = (CoreHandle *) stream_.apiHandle; - if ( stream_.mode == OUTPUT || stream_.mode == DUPLEX ) { - if (handle) { - AudioObjectPropertyAddress property = { kAudioHardwarePropertyDevices, - kAudioObjectPropertyScopeGlobal, - kAudioObjectPropertyElementMaster }; - - property.mSelector = kAudioDeviceProcessorOverload; - property.mScope = kAudioObjectPropertyScopeGlobal; - if (AudioObjectRemovePropertyListener( handle->id[0], &property, xrunListener, (void *) handle ) != noErr) { - errorText_ = "RtApiCore::closeStream(): error removing property listener!"; - error( RtAudioError::WARNING ); - } - } - if ( stream_.state == STREAM_RUNNING ) - AudioDeviceStop( handle->id[0], callbackHandler ); -#if defined( MAC_OS_X_VERSION_10_5 ) && ( MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_5 ) - AudioDeviceDestroyIOProcID( handle->id[0], handle->procId[0] ); -#else - // deprecated in favor of AudioDeviceDestroyIOProcID() - AudioDeviceRemoveIOProc( handle->id[0], callbackHandler ); -#endif - } - - if ( stream_.mode == INPUT || ( stream_.mode == DUPLEX && stream_.device[0] != stream_.device[1] ) ) { - if (handle) { - AudioObjectPropertyAddress property = { kAudioHardwarePropertyDevices, - kAudioObjectPropertyScopeGlobal, - kAudioObjectPropertyElementMaster }; - - property.mSelector = kAudioDeviceProcessorOverload; - property.mScope = kAudioObjectPropertyScopeGlobal; - if (AudioObjectRemovePropertyListener( handle->id[1], &property, xrunListener, (void *) handle ) != noErr) { - errorText_ = "RtApiCore::closeStream(): error removing property listener!"; - error( RtAudioError::WARNING ); - } - } - if ( stream_.state == STREAM_RUNNING ) - AudioDeviceStop( handle->id[1], callbackHandler ); -#if defined( MAC_OS_X_VERSION_10_5 ) && ( MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_5 ) - AudioDeviceDestroyIOProcID( handle->id[1], handle->procId[1] ); -#else - // deprecated in favor of AudioDeviceDestroyIOProcID() - AudioDeviceRemoveIOProc( handle->id[1], callbackHandler ); -#endif - } - - for ( int i=0; i<2; i++ ) { - if ( stream_.userBuffer[i] ) { - free( stream_.userBuffer[i] ); - stream_.userBuffer[i] = 0; - } - } - - if ( stream_.deviceBuffer ) { - free( stream_.deviceBuffer ); - stream_.deviceBuffer = 0; - } - - // Destroy pthread condition variable. - pthread_cond_destroy( &handle->condition ); - delete handle; - stream_.apiHandle = 0; - - stream_.mode = UNINITIALIZED; - stream_.state = STREAM_CLOSED; -} - -void RtApiCore :: startStream( void ) -{ - verifyStream(); - if ( stream_.state == STREAM_RUNNING ) { - errorText_ = "RtApiCore::startStream(): the stream is already running!"; - error( RtAudioError::WARNING ); - return; - } - - #if defined( HAVE_GETTIMEOFDAY ) - gettimeofday( &stream_.lastTickTimestamp, NULL ); - #endif - - OSStatus result = noErr; - CoreHandle *handle = (CoreHandle *) stream_.apiHandle; - if ( stream_.mode == OUTPUT || stream_.mode == DUPLEX ) { - - result = AudioDeviceStart( handle->id[0], callbackHandler ); - if ( result != noErr ) { - errorStream_ << "RtApiCore::startStream: system error (" << getErrorCode( result ) << ") starting callback procedure on device (" << stream_.device[0] << ")."; - errorText_ = errorStream_.str(); - goto unlock; - } - } - - if ( stream_.mode == INPUT || - ( stream_.mode == DUPLEX && stream_.device[0] != stream_.device[1] ) ) { - - result = AudioDeviceStart( handle->id[1], callbackHandler ); - if ( result != noErr ) { - errorStream_ << "RtApiCore::startStream: system error starting input callback procedure on device (" << stream_.device[1] << ")."; - errorText_ = errorStream_.str(); - goto unlock; - } - } - - handle->drainCounter = 0; - handle->internalDrain = false; - stream_.state = STREAM_RUNNING; - - unlock: - if ( result == noErr ) return; - error( RtAudioError::SYSTEM_ERROR ); -} - -void RtApiCore :: stopStream( void ) -{ - verifyStream(); - if ( stream_.state == STREAM_STOPPED ) { - errorText_ = "RtApiCore::stopStream(): the stream is already stopped!"; - error( RtAudioError::WARNING ); - return; - } - - OSStatus result = noErr; - CoreHandle *handle = (CoreHandle *) stream_.apiHandle; - if ( stream_.mode == OUTPUT || stream_.mode == DUPLEX ) { - - if ( handle->drainCounter == 0 ) { - handle->drainCounter = 2; - pthread_cond_wait( &handle->condition, &stream_.mutex ); // block until signaled - } - - result = AudioDeviceStop( handle->id[0], callbackHandler ); - if ( result != noErr ) { - errorStream_ << "RtApiCore::stopStream: system error (" << getErrorCode( result ) << ") stopping callback procedure on device (" << stream_.device[0] << ")."; - errorText_ = errorStream_.str(); - goto unlock; - } - } - - if ( stream_.mode == INPUT || ( stream_.mode == DUPLEX && stream_.device[0] != stream_.device[1] ) ) { - - result = AudioDeviceStop( handle->id[1], callbackHandler ); - if ( result != noErr ) { - errorStream_ << "RtApiCore::stopStream: system error (" << getErrorCode( result ) << ") stopping input callback procedure on device (" << stream_.device[1] << ")."; - errorText_ = errorStream_.str(); - goto unlock; - } - } - - stream_.state = STREAM_STOPPED; - - unlock: - if ( result == noErr ) return; - error( RtAudioError::SYSTEM_ERROR ); -} - -void RtApiCore :: abortStream( void ) -{ - verifyStream(); - if ( stream_.state == STREAM_STOPPED ) { - errorText_ = "RtApiCore::abortStream(): the stream is already stopped!"; - error( RtAudioError::WARNING ); - return; - } - - CoreHandle *handle = (CoreHandle *) stream_.apiHandle; - handle->drainCounter = 2; - - stopStream(); -} - -// This function will be called by a spawned thread when the user -// callback function signals that the stream should be stopped or -// aborted. It is better to handle it this way because the -// callbackEvent() function probably should return before the AudioDeviceStop() -// function is called. -static void *coreStopStream( void *ptr ) -{ - CallbackInfo *info = (CallbackInfo *) ptr; - RtApiCore *object = (RtApiCore *) info->object; - - object->stopStream(); - pthread_exit( NULL ); -} - -bool RtApiCore :: callbackEvent( AudioDeviceID deviceId, - const AudioBufferList *inBufferList, - const AudioBufferList *outBufferList ) -{ - if ( stream_.state == STREAM_STOPPED || stream_.state == STREAM_STOPPING ) return SUCCESS; - if ( stream_.state == STREAM_CLOSED ) { - errorText_ = "RtApiCore::callbackEvent(): the stream is closed ... this shouldn't happen!"; - error( RtAudioError::WARNING ); - return FAILURE; - } - - CallbackInfo *info = (CallbackInfo *) &stream_.callbackInfo; - CoreHandle *handle = (CoreHandle *) stream_.apiHandle; - - // Check if we were draining the stream and signal is finished. - if ( handle->drainCounter > 3 ) { - ThreadHandle threadId; - - stream_.state = STREAM_STOPPING; - if ( handle->internalDrain == true ) - pthread_create( &threadId, NULL, coreStopStream, info ); - else // external call to stopStream() - pthread_cond_signal( &handle->condition ); - return SUCCESS; - } - - AudioDeviceID outputDevice = handle->id[0]; - - // Invoke user callback to get fresh output data UNLESS we are - // draining stream or duplex mode AND the input/output devices are - // different AND this function is called for the input device. - if ( handle->drainCounter == 0 && ( stream_.mode != DUPLEX || deviceId == outputDevice ) ) { - RtAudioCallback callback = (RtAudioCallback) info->callback; - double streamTime = getStreamTime(); - RtAudioStreamStatus status = 0; - if ( stream_.mode != INPUT && handle->xrun[0] == true ) { - status |= RTAUDIO_OUTPUT_UNDERFLOW; - handle->xrun[0] = false; - } - if ( stream_.mode != OUTPUT && handle->xrun[1] == true ) { - status |= RTAUDIO_INPUT_OVERFLOW; - handle->xrun[1] = false; - } - - int cbReturnValue = callback( stream_.userBuffer[0], stream_.userBuffer[1], - stream_.bufferSize, streamTime, status, info->userData ); - if ( cbReturnValue == 2 ) { - stream_.state = STREAM_STOPPING; - handle->drainCounter = 2; - abortStream(); - return SUCCESS; - } - else if ( cbReturnValue == 1 ) { - handle->drainCounter = 1; - handle->internalDrain = true; - } - } - - if ( stream_.mode == OUTPUT || ( stream_.mode == DUPLEX && deviceId == outputDevice ) ) { - - if ( handle->drainCounter > 1 ) { // write zeros to the output stream - - if ( handle->nStreams[0] == 1 ) { - memset( outBufferList->mBuffers[handle->iStream[0]].mData, - 0, - outBufferList->mBuffers[handle->iStream[0]].mDataByteSize ); - } - else { // fill multiple streams with zeros - for ( unsigned int i=0; inStreams[0]; i++ ) { - memset( outBufferList->mBuffers[handle->iStream[0]+i].mData, - 0, - outBufferList->mBuffers[handle->iStream[0]+i].mDataByteSize ); - } - } - } - else if ( handle->nStreams[0] == 1 ) { - if ( stream_.doConvertBuffer[0] ) { // convert directly to CoreAudio stream buffer - convertBuffer( (char *) outBufferList->mBuffers[handle->iStream[0]].mData, - stream_.userBuffer[0], stream_.convertInfo[0] ); - } - else { // copy from user buffer - memcpy( outBufferList->mBuffers[handle->iStream[0]].mData, - stream_.userBuffer[0], - outBufferList->mBuffers[handle->iStream[0]].mDataByteSize ); - } - } - else { // fill multiple streams - Float32 *inBuffer = (Float32 *) stream_.userBuffer[0]; - if ( stream_.doConvertBuffer[0] ) { - convertBuffer( stream_.deviceBuffer, stream_.userBuffer[0], stream_.convertInfo[0] ); - inBuffer = (Float32 *) stream_.deviceBuffer; - } - - if ( stream_.deviceInterleaved[0] == false ) { // mono mode - UInt32 bufferBytes = outBufferList->mBuffers[handle->iStream[0]].mDataByteSize; - for ( unsigned int i=0; imBuffers[handle->iStream[0]+i].mData, - (void *)&inBuffer[i*stream_.bufferSize], bufferBytes ); - } - } - else { // fill multiple multi-channel streams with interleaved data - UInt32 streamChannels, channelsLeft, inJump, outJump, inOffset; - Float32 *out, *in; - - bool inInterleaved = ( stream_.userInterleaved ) ? true : false; - UInt32 inChannels = stream_.nUserChannels[0]; - if ( stream_.doConvertBuffer[0] ) { - inInterleaved = true; // device buffer will always be interleaved for nStreams > 1 and not mono mode - inChannels = stream_.nDeviceChannels[0]; - } - - if ( inInterleaved ) inOffset = 1; - else inOffset = stream_.bufferSize; - - channelsLeft = inChannels; - for ( unsigned int i=0; inStreams[0]; i++ ) { - in = inBuffer; - out = (Float32 *) outBufferList->mBuffers[handle->iStream[0]+i].mData; - streamChannels = outBufferList->mBuffers[handle->iStream[0]+i].mNumberChannels; - - outJump = 0; - // Account for possible channel offset in first stream - if ( i == 0 && stream_.channelOffset[0] > 0 ) { - streamChannels -= stream_.channelOffset[0]; - outJump = stream_.channelOffset[0]; - out += outJump; - } - - // Account for possible unfilled channels at end of the last stream - if ( streamChannels > channelsLeft ) { - outJump = streamChannels - channelsLeft; - streamChannels = channelsLeft; - } - - // Determine input buffer offsets and skips - if ( inInterleaved ) { - inJump = inChannels; - in += inChannels - channelsLeft; - } - else { - inJump = 1; - in += (inChannels - channelsLeft) * inOffset; - } - - for ( unsigned int i=0; idrainCounter ) { - handle->drainCounter++; - goto unlock; - } - - AudioDeviceID inputDevice; - inputDevice = handle->id[1]; - if ( stream_.mode == INPUT || ( stream_.mode == DUPLEX && deviceId == inputDevice ) ) { - - if ( handle->nStreams[1] == 1 ) { - if ( stream_.doConvertBuffer[1] ) { // convert directly from CoreAudio stream buffer - convertBuffer( stream_.userBuffer[1], - (char *) inBufferList->mBuffers[handle->iStream[1]].mData, - stream_.convertInfo[1] ); - } - else { // copy to user buffer - memcpy( stream_.userBuffer[1], - inBufferList->mBuffers[handle->iStream[1]].mData, - inBufferList->mBuffers[handle->iStream[1]].mDataByteSize ); - } - } - else { // read from multiple streams - Float32 *outBuffer = (Float32 *) stream_.userBuffer[1]; - if ( stream_.doConvertBuffer[1] ) outBuffer = (Float32 *) stream_.deviceBuffer; - - if ( stream_.deviceInterleaved[1] == false ) { // mono mode - UInt32 bufferBytes = inBufferList->mBuffers[handle->iStream[1]].mDataByteSize; - for ( unsigned int i=0; imBuffers[handle->iStream[1]+i].mData, bufferBytes ); - } - } - else { // read from multiple multi-channel streams - UInt32 streamChannels, channelsLeft, inJump, outJump, outOffset; - Float32 *out, *in; - - bool outInterleaved = ( stream_.userInterleaved ) ? true : false; - UInt32 outChannels = stream_.nUserChannels[1]; - if ( stream_.doConvertBuffer[1] ) { - outInterleaved = true; // device buffer will always be interleaved for nStreams > 1 and not mono mode - outChannels = stream_.nDeviceChannels[1]; - } - - if ( outInterleaved ) outOffset = 1; - else outOffset = stream_.bufferSize; - - channelsLeft = outChannels; - for ( unsigned int i=0; inStreams[1]; i++ ) { - out = outBuffer; - in = (Float32 *) inBufferList->mBuffers[handle->iStream[1]+i].mData; - streamChannels = inBufferList->mBuffers[handle->iStream[1]+i].mNumberChannels; - - inJump = 0; - // Account for possible channel offset in first stream - if ( i == 0 && stream_.channelOffset[1] > 0 ) { - streamChannels -= stream_.channelOffset[1]; - inJump = stream_.channelOffset[1]; - in += inJump; - } - - // Account for possible unread channels at end of the last stream - if ( streamChannels > channelsLeft ) { - inJump = streamChannels - channelsLeft; - streamChannels = channelsLeft; - } - - // Determine output buffer offsets and skips - if ( outInterleaved ) { - outJump = outChannels; - out += outChannels - channelsLeft; - } - else { - outJump = 1; - out += (outChannels - channelsLeft) * outOffset; - } - - for ( unsigned int i=0; iid[0] != handle->id[1] && deviceId == handle->id[0] ) ) - RtApi::tickStreamTime(); - - return SUCCESS; -} - -const char* RtApiCore :: getErrorCode( OSStatus code ) -{ - switch( code ) { - - case kAudioHardwareNotRunningError: - return "kAudioHardwareNotRunningError"; - - case kAudioHardwareUnspecifiedError: - return "kAudioHardwareUnspecifiedError"; - - case kAudioHardwareUnknownPropertyError: - return "kAudioHardwareUnknownPropertyError"; - - case kAudioHardwareBadPropertySizeError: - return "kAudioHardwareBadPropertySizeError"; - - case kAudioHardwareIllegalOperationError: - return "kAudioHardwareIllegalOperationError"; - - case kAudioHardwareBadObjectError: - return "kAudioHardwareBadObjectError"; - - case kAudioHardwareBadDeviceError: - return "kAudioHardwareBadDeviceError"; - - case kAudioHardwareBadStreamError: - return "kAudioHardwareBadStreamError"; - - case kAudioHardwareUnsupportedOperationError: - return "kAudioHardwareUnsupportedOperationError"; - - case kAudioDeviceUnsupportedFormatError: - return "kAudioDeviceUnsupportedFormatError"; - - case kAudioDevicePermissionsError: - return "kAudioDevicePermissionsError"; - - default: - return "CoreAudio unknown error"; - } -} - - //******************** End of __MACOSX_CORE__ *********************// -#endif - -#if defined(__UNIX_JACK__) - -// JACK is a low-latency audio server, originally written for the -// GNU/Linux operating system and now also ported to OS-X. It can -// connect a number of different applications to an audio device, as -// well as allowing them to share audio between themselves. -// -// When using JACK with RtAudio, "devices" refer to JACK clients that -// have ports connected to the server. The JACK server is typically -// started in a terminal as follows: -// -// .jackd -d alsa -d hw:0 -// -// or through an interface program such as qjackctl. Many of the -// parameters normally set for a stream are fixed by the JACK server -// and can be specified when the JACK server is started. In -// particular, -// -// .jackd -d alsa -d hw:0 -r 44100 -p 512 -n 4 -// -// specifies a sample rate of 44100 Hz, a buffer size of 512 sample -// frames, and number of buffers = 4. Once the server is running, it -// is not possible to override these values. If the values are not -// specified in the command-line, the JACK server uses default values. -// -// The JACK server does not have to be running when an instance of -// RtApiJack is created, though the function getDeviceCount() will -// report 0 devices found until JACK has been started. When no -// devices are available (i.e., the JACK server is not running), a -// stream cannot be opened. - -#include -#include -#include - -// A structure to hold various information related to the Jack API -// implementation. -struct JackHandle { - jack_client_t *client; - jack_port_t **ports[2]; - std::string deviceName[2]; - bool xrun[2]; - pthread_cond_t condition; - int drainCounter; // Tracks callback counts when draining - bool internalDrain; // Indicates if stop is initiated from callback or not. - - JackHandle() - :client(0), drainCounter(0), internalDrain(false) { ports[0] = 0; ports[1] = 0; xrun[0] = false; xrun[1] = false; } -}; - -#if !defined(__RTAUDIO_DEBUG__) -static void jackSilentError( const char * ) {} -#endif - -RtApiJack :: RtApiJack() - :shouldAutoconnect_(true) { - // Nothing to do here. -#if !defined(__RTAUDIO_DEBUG__) - // Turn off Jack's internal error reporting. - jack_set_error_function( &jackSilentError ); -#endif -} - -RtApiJack :: ~RtApiJack() -{ - if ( stream_.state != STREAM_CLOSED ) closeStream(); -} - -unsigned int RtApiJack :: getDeviceCount( void ) -{ - // See if we can become a jack client. - jack_options_t options = (jack_options_t) ( JackNoStartServer ); //JackNullOption; - jack_status_t *status = NULL; - jack_client_t *client = jack_client_open( "RtApiJackCount", options, status ); - if ( client == 0 ) return 0; - - const char **ports; - std::string port, previousPort; - unsigned int nChannels = 0, nDevices = 0; - ports = jack_get_ports( client, NULL, JACK_DEFAULT_AUDIO_TYPE, 0 ); - if ( ports ) { - // Parse the port names up to the first colon (:). - size_t iColon = 0; - do { - port = (char *) ports[ nChannels ]; - iColon = port.find(":"); - if ( iColon != std::string::npos ) { - port = port.substr( 0, iColon + 1 ); - if ( port != previousPort ) { - nDevices++; - previousPort = port; - } - } - } while ( ports[++nChannels] ); - free( ports ); - } - - jack_client_close( client ); - return nDevices; -} - -RtAudio::DeviceInfo RtApiJack :: getDeviceInfo( unsigned int device ) -{ - RtAudio::DeviceInfo info; - info.probed = false; - - jack_options_t options = (jack_options_t) ( JackNoStartServer ); //JackNullOption - jack_status_t *status = NULL; - jack_client_t *client = jack_client_open( "RtApiJackInfo", options, status ); - if ( client == 0 ) { - errorText_ = "RtApiJack::getDeviceInfo: Jack server not found or connection error!"; - error( RtAudioError::WARNING ); - return info; - } - - const char **ports; - std::string port, previousPort; - unsigned int nPorts = 0, nDevices = 0; - ports = jack_get_ports( client, NULL, JACK_DEFAULT_AUDIO_TYPE, 0 ); - if ( ports ) { - // Parse the port names up to the first colon (:). - size_t iColon = 0; - do { - port = (char *) ports[ nPorts ]; - iColon = port.find(":"); - if ( iColon != std::string::npos ) { - port = port.substr( 0, iColon ); - if ( port != previousPort ) { - if ( nDevices == device ) info.name = port; - nDevices++; - previousPort = port; - } - } - } while ( ports[++nPorts] ); - free( ports ); - } - - if ( device >= nDevices ) { - jack_client_close( client ); - errorText_ = "RtApiJack::getDeviceInfo: device ID is invalid!"; - error( RtAudioError::INVALID_USE ); - return info; - } - - // Get the current jack server sample rate. - info.sampleRates.clear(); - - info.preferredSampleRate = jack_get_sample_rate( client ); - info.sampleRates.push_back( info.preferredSampleRate ); - - // Count the available ports containing the client name as device - // channels. Jack "input ports" equal RtAudio output channels. - unsigned int nChannels = 0; - ports = jack_get_ports( client, info.name.c_str(), JACK_DEFAULT_AUDIO_TYPE, JackPortIsInput ); - if ( ports ) { - while ( ports[ nChannels ] ) nChannels++; - free( ports ); - info.outputChannels = nChannels; - } - - // Jack "output ports" equal RtAudio input channels. - nChannels = 0; - ports = jack_get_ports( client, info.name.c_str(), JACK_DEFAULT_AUDIO_TYPE, JackPortIsOutput ); - if ( ports ) { - while ( ports[ nChannels ] ) nChannels++; - free( ports ); - info.inputChannels = nChannels; - } - - if ( info.outputChannels == 0 && info.inputChannels == 0 ) { - jack_client_close(client); - errorText_ = "RtApiJack::getDeviceInfo: error determining Jack input/output channels!"; - error( RtAudioError::WARNING ); - return info; - } - - // If device opens for both playback and capture, we determine the channels. - if ( info.outputChannels > 0 && info.inputChannels > 0 ) - info.duplexChannels = (info.outputChannels > info.inputChannels) ? info.inputChannels : info.outputChannels; - - // Jack always uses 32-bit floats. - info.nativeFormats = RTAUDIO_FLOAT32; - - // Jack doesn't provide default devices so we'll use the first available one. - if ( device == 0 && info.outputChannels > 0 ) - info.isDefaultOutput = true; - if ( device == 0 && info.inputChannels > 0 ) - info.isDefaultInput = true; - - jack_client_close(client); - info.probed = true; - return info; -} - -static int jackCallbackHandler( jack_nframes_t nframes, void *infoPointer ) -{ - CallbackInfo *info = (CallbackInfo *) infoPointer; - - RtApiJack *object = (RtApiJack *) info->object; - if ( object->callbackEvent( (unsigned long) nframes ) == false ) return 1; - - return 0; -} - -// This function will be called by a spawned thread when the Jack -// server signals that it is shutting down. It is necessary to handle -// it this way because the jackShutdown() function must return before -// the jack_deactivate() function (in closeStream()) will return. -static void *jackCloseStream( void *ptr ) -{ - CallbackInfo *info = (CallbackInfo *) ptr; - RtApiJack *object = (RtApiJack *) info->object; - - object->closeStream(); - - pthread_exit( NULL ); -} -static void jackShutdown( void *infoPointer ) -{ - CallbackInfo *info = (CallbackInfo *) infoPointer; - RtApiJack *object = (RtApiJack *) info->object; - - // Check current stream state. If stopped, then we'll assume this - // was called as a result of a call to RtApiJack::stopStream (the - // deactivation of a client handle causes this function to be called). - // If not, we'll assume the Jack server is shutting down or some - // other problem occurred and we should close the stream. - if ( object->isStreamRunning() == false ) return; - - ThreadHandle threadId; - pthread_create( &threadId, NULL, jackCloseStream, info ); - std::cerr << "\nRtApiJack: the Jack server is shutting down this client ... stream stopped and closed!!\n" << std::endl; -} - -static int jackXrun( void *infoPointer ) -{ - JackHandle *handle = *((JackHandle **) infoPointer); - - if ( handle->ports[0] ) handle->xrun[0] = true; - if ( handle->ports[1] ) handle->xrun[1] = true; - - return 0; -} - -bool RtApiJack :: probeDeviceOpen( unsigned int device, StreamMode mode, unsigned int channels, - unsigned int firstChannel, unsigned int sampleRate, - RtAudioFormat format, unsigned int *bufferSize, - RtAudio::StreamOptions *options ) -{ - JackHandle *handle = (JackHandle *) stream_.apiHandle; - - // Look for jack server and try to become a client (only do once per stream). - jack_client_t *client = 0; - if ( mode == OUTPUT || ( mode == INPUT && stream_.mode != OUTPUT ) ) { - jack_options_t jackoptions = (jack_options_t) ( JackNoStartServer ); //JackNullOption; - jack_status_t *status = NULL; - if ( options && !options->streamName.empty() ) - client = jack_client_open( options->streamName.c_str(), jackoptions, status ); - else - client = jack_client_open( "RtApiJack", jackoptions, status ); - if ( client == 0 ) { - errorText_ = "RtApiJack::probeDeviceOpen: Jack server not found or connection error!"; - error( RtAudioError::WARNING ); - return FAILURE; - } - } - else { - // The handle must have been created on an earlier pass. - client = handle->client; - } - - const char **ports; - std::string port, previousPort, deviceName; - unsigned int nPorts = 0, nDevices = 0; - ports = jack_get_ports( client, NULL, JACK_DEFAULT_AUDIO_TYPE, 0 ); - if ( ports ) { - // Parse the port names up to the first colon (:). - size_t iColon = 0; - do { - port = (char *) ports[ nPorts ]; - iColon = port.find(":"); - if ( iColon != std::string::npos ) { - port = port.substr( 0, iColon ); - if ( port != previousPort ) { - if ( nDevices == device ) deviceName = port; - nDevices++; - previousPort = port; - } - } - } while ( ports[++nPorts] ); - free( ports ); - } - - if ( device >= nDevices ) { - errorText_ = "RtApiJack::probeDeviceOpen: device ID is invalid!"; - return FAILURE; - } - - unsigned long flag = JackPortIsInput; - if ( mode == INPUT ) flag = JackPortIsOutput; - - if ( ! (options && (options->flags & RTAUDIO_JACK_DONT_CONNECT)) ) { - // Count the available ports containing the client name as device - // channels. Jack "input ports" equal RtAudio output channels. - unsigned int nChannels = 0; - ports = jack_get_ports( client, deviceName.c_str(), JACK_DEFAULT_AUDIO_TYPE, flag ); - if ( ports ) { - while ( ports[ nChannels ] ) nChannels++; - free( ports ); - } - // Compare the jack ports for specified client to the requested number of channels. - if ( nChannels < (channels + firstChannel) ) { - errorStream_ << "RtApiJack::probeDeviceOpen: requested number of channels (" << channels << ") + offset (" << firstChannel << ") not found for specified device (" << device << ":" << deviceName << ")."; - errorText_ = errorStream_.str(); - return FAILURE; - } - } - - // Check the jack server sample rate. - unsigned int jackRate = jack_get_sample_rate( client ); - if ( sampleRate != jackRate ) { - jack_client_close( client ); - errorStream_ << "RtApiJack::probeDeviceOpen: the requested sample rate (" << sampleRate << ") is different than the JACK server rate (" << jackRate << ")."; - errorText_ = errorStream_.str(); - return FAILURE; - } - stream_.sampleRate = jackRate; - - // Get the latency of the JACK port. - ports = jack_get_ports( client, deviceName.c_str(), JACK_DEFAULT_AUDIO_TYPE, flag ); - if ( ports[ firstChannel ] ) { - // Added by Ge Wang - jack_latency_callback_mode_t cbmode = (mode == INPUT ? JackCaptureLatency : JackPlaybackLatency); - // the range (usually the min and max are equal) - jack_latency_range_t latrange; latrange.min = latrange.max = 0; - // get the latency range - jack_port_get_latency_range( jack_port_by_name( client, ports[firstChannel] ), cbmode, &latrange ); - // be optimistic, use the min! - stream_.latency[mode] = latrange.min; - //stream_.latency[mode] = jack_port_get_latency( jack_port_by_name( client, ports[ firstChannel ] ) ); - } - free( ports ); - - // The jack server always uses 32-bit floating-point data. - stream_.deviceFormat[mode] = RTAUDIO_FLOAT32; - stream_.userFormat = format; - - if ( options && options->flags & RTAUDIO_NONINTERLEAVED ) stream_.userInterleaved = false; - else stream_.userInterleaved = true; - - // Jack always uses non-interleaved buffers. - stream_.deviceInterleaved[mode] = false; - - // Jack always provides host byte-ordered data. - stream_.doByteSwap[mode] = false; - - // Get the buffer size. The buffer size and number of buffers - // (periods) is set when the jack server is started. - stream_.bufferSize = (int) jack_get_buffer_size( client ); - *bufferSize = stream_.bufferSize; - - stream_.nDeviceChannels[mode] = channels; - stream_.nUserChannels[mode] = channels; - - // Set flags for buffer conversion. - stream_.doConvertBuffer[mode] = false; - if ( stream_.userFormat != stream_.deviceFormat[mode] ) - stream_.doConvertBuffer[mode] = true; - if ( stream_.userInterleaved != stream_.deviceInterleaved[mode] && - stream_.nUserChannels[mode] > 1 ) - stream_.doConvertBuffer[mode] = true; - - // Allocate our JackHandle structure for the stream. - if ( handle == 0 ) { - try { - handle = new JackHandle; - } - catch ( std::bad_alloc& ) { - errorText_ = "RtApiJack::probeDeviceOpen: error allocating JackHandle memory."; - goto error; - } - - if ( pthread_cond_init(&handle->condition, NULL) ) { - errorText_ = "RtApiJack::probeDeviceOpen: error initializing pthread condition variable."; - goto error; - } - stream_.apiHandle = (void *) handle; - handle->client = client; - } - handle->deviceName[mode] = deviceName; - - // Allocate necessary internal buffers. - unsigned long bufferBytes; - bufferBytes = stream_.nUserChannels[mode] * *bufferSize * formatBytes( stream_.userFormat ); - stream_.userBuffer[mode] = (char *) calloc( bufferBytes, 1 ); - if ( stream_.userBuffer[mode] == NULL ) { - errorText_ = "RtApiJack::probeDeviceOpen: error allocating user buffer memory."; - goto error; - } - - if ( stream_.doConvertBuffer[mode] ) { - - bool makeBuffer = true; - if ( mode == OUTPUT ) - bufferBytes = stream_.nDeviceChannels[0] * formatBytes( stream_.deviceFormat[0] ); - else { // mode == INPUT - bufferBytes = stream_.nDeviceChannels[1] * formatBytes( stream_.deviceFormat[1] ); - if ( stream_.mode == OUTPUT && stream_.deviceBuffer ) { - unsigned long bytesOut = stream_.nDeviceChannels[0] * formatBytes(stream_.deviceFormat[0]); - if ( bufferBytes < bytesOut ) makeBuffer = false; - } - } - - if ( makeBuffer ) { - bufferBytes *= *bufferSize; - if ( stream_.deviceBuffer ) free( stream_.deviceBuffer ); - stream_.deviceBuffer = (char *) calloc( bufferBytes, 1 ); - if ( stream_.deviceBuffer == NULL ) { - errorText_ = "RtApiJack::probeDeviceOpen: error allocating device buffer memory."; - goto error; - } - } - } - - // Allocate memory for the Jack ports (channels) identifiers. - handle->ports[mode] = (jack_port_t **) malloc ( sizeof (jack_port_t *) * channels ); - if ( handle->ports[mode] == NULL ) { - errorText_ = "RtApiJack::probeDeviceOpen: error allocating port memory."; - goto error; - } - - stream_.device[mode] = device; - stream_.channelOffset[mode] = firstChannel; - stream_.state = STREAM_STOPPED; - stream_.callbackInfo.object = (void *) this; - - if ( stream_.mode == OUTPUT && mode == INPUT ) - // We had already set up the stream for output. - stream_.mode = DUPLEX; - else { - stream_.mode = mode; - jack_set_process_callback( handle->client, jackCallbackHandler, (void *) &stream_.callbackInfo ); - jack_set_xrun_callback( handle->client, jackXrun, (void *) &stream_.apiHandle ); - jack_on_shutdown( handle->client, jackShutdown, (void *) &stream_.callbackInfo ); - } - - // Register our ports. - char label[64]; - if ( mode == OUTPUT ) { - for ( unsigned int i=0; iports[0][i] = jack_port_register( handle->client, (const char *)label, - JACK_DEFAULT_AUDIO_TYPE, JackPortIsOutput, 0 ); - } - } - else { - for ( unsigned int i=0; iports[1][i] = jack_port_register( handle->client, (const char *)label, - JACK_DEFAULT_AUDIO_TYPE, JackPortIsInput, 0 ); - } - } - - // Setup the buffer conversion information structure. We don't use - // buffers to do channel offsets, so we override that parameter - // here. - if ( stream_.doConvertBuffer[mode] ) setConvertInfo( mode, 0 ); - - if ( options && options->flags & RTAUDIO_JACK_DONT_CONNECT ) shouldAutoconnect_ = false; - - return SUCCESS; - - error: - if ( handle ) { - pthread_cond_destroy( &handle->condition ); - jack_client_close( handle->client ); - - if ( handle->ports[0] ) free( handle->ports[0] ); - if ( handle->ports[1] ) free( handle->ports[1] ); - - delete handle; - stream_.apiHandle = 0; - } - - for ( int i=0; i<2; i++ ) { - if ( stream_.userBuffer[i] ) { - free( stream_.userBuffer[i] ); - stream_.userBuffer[i] = 0; - } - } - - if ( stream_.deviceBuffer ) { - free( stream_.deviceBuffer ); - stream_.deviceBuffer = 0; - } - - return FAILURE; -} - -void RtApiJack :: closeStream( void ) -{ - if ( stream_.state == STREAM_CLOSED ) { - errorText_ = "RtApiJack::closeStream(): no open stream to close!"; - error( RtAudioError::WARNING ); - return; - } - - JackHandle *handle = (JackHandle *) stream_.apiHandle; - if ( handle ) { - - if ( stream_.state == STREAM_RUNNING ) - jack_deactivate( handle->client ); - - jack_client_close( handle->client ); - } - - if ( handle ) { - if ( handle->ports[0] ) free( handle->ports[0] ); - if ( handle->ports[1] ) free( handle->ports[1] ); - pthread_cond_destroy( &handle->condition ); - delete handle; - stream_.apiHandle = 0; - } - - for ( int i=0; i<2; i++ ) { - if ( stream_.userBuffer[i] ) { - free( stream_.userBuffer[i] ); - stream_.userBuffer[i] = 0; - } - } - - if ( stream_.deviceBuffer ) { - free( stream_.deviceBuffer ); - stream_.deviceBuffer = 0; - } - - stream_.mode = UNINITIALIZED; - stream_.state = STREAM_CLOSED; -} - -void RtApiJack :: startStream( void ) -{ - verifyStream(); - if ( stream_.state == STREAM_RUNNING ) { - errorText_ = "RtApiJack::startStream(): the stream is already running!"; - error( RtAudioError::WARNING ); - return; - } - - #if defined( HAVE_GETTIMEOFDAY ) - gettimeofday( &stream_.lastTickTimestamp, NULL ); - #endif - - JackHandle *handle = (JackHandle *) stream_.apiHandle; - int result = jack_activate( handle->client ); - if ( result ) { - errorText_ = "RtApiJack::startStream(): unable to activate JACK client!"; - goto unlock; - } - - const char **ports; - - // Get the list of available ports. - if ( shouldAutoconnect_ && (stream_.mode == OUTPUT || stream_.mode == DUPLEX) ) { - result = 1; - ports = jack_get_ports( handle->client, handle->deviceName[0].c_str(), JACK_DEFAULT_AUDIO_TYPE, JackPortIsInput); - if ( ports == NULL) { - errorText_ = "RtApiJack::startStream(): error determining available JACK input ports!"; - goto unlock; - } - - // Now make the port connections. Since RtAudio wasn't designed to - // allow the user to select particular channels of a device, we'll - // just open the first "nChannels" ports with offset. - for ( unsigned int i=0; iclient, jack_port_name( handle->ports[0][i] ), ports[ stream_.channelOffset[0] + i ] ); - if ( result ) { - free( ports ); - errorText_ = "RtApiJack::startStream(): error connecting output ports!"; - goto unlock; - } - } - free(ports); - } - - if ( shouldAutoconnect_ && (stream_.mode == INPUT || stream_.mode == DUPLEX) ) { - result = 1; - ports = jack_get_ports( handle->client, handle->deviceName[1].c_str(), JACK_DEFAULT_AUDIO_TYPE, JackPortIsOutput ); - if ( ports == NULL) { - errorText_ = "RtApiJack::startStream(): error determining available JACK output ports!"; - goto unlock; - } - - // Now make the port connections. See note above. - for ( unsigned int i=0; iclient, ports[ stream_.channelOffset[1] + i ], jack_port_name( handle->ports[1][i] ) ); - if ( result ) { - free( ports ); - errorText_ = "RtApiJack::startStream(): error connecting input ports!"; - goto unlock; - } - } - free(ports); - } - - handle->drainCounter = 0; - handle->internalDrain = false; - stream_.state = STREAM_RUNNING; - - unlock: - if ( result == 0 ) return; - error( RtAudioError::SYSTEM_ERROR ); -} - -void RtApiJack :: stopStream( void ) -{ - verifyStream(); - if ( stream_.state == STREAM_STOPPED ) { - errorText_ = "RtApiJack::stopStream(): the stream is already stopped!"; - error( RtAudioError::WARNING ); - return; - } - - JackHandle *handle = (JackHandle *) stream_.apiHandle; - if ( stream_.mode == OUTPUT || stream_.mode == DUPLEX ) { - - if ( handle->drainCounter == 0 ) { - handle->drainCounter = 2; - pthread_cond_wait( &handle->condition, &stream_.mutex ); // block until signaled - } - } - - jack_deactivate( handle->client ); - stream_.state = STREAM_STOPPED; -} - -void RtApiJack :: abortStream( void ) -{ - verifyStream(); - if ( stream_.state == STREAM_STOPPED ) { - errorText_ = "RtApiJack::abortStream(): the stream is already stopped!"; - error( RtAudioError::WARNING ); - return; - } - - JackHandle *handle = (JackHandle *) stream_.apiHandle; - handle->drainCounter = 2; - - stopStream(); -} - -// This function will be called by a spawned thread when the user -// callback function signals that the stream should be stopped or -// aborted. It is necessary to handle it this way because the -// callbackEvent() function must return before the jack_deactivate() -// function will return. -static void *jackStopStream( void *ptr ) -{ - CallbackInfo *info = (CallbackInfo *) ptr; - RtApiJack *object = (RtApiJack *) info->object; - - object->stopStream(); - pthread_exit( NULL ); -} - -bool RtApiJack :: callbackEvent( unsigned long nframes ) -{ - if ( stream_.state == STREAM_STOPPED || stream_.state == STREAM_STOPPING ) return SUCCESS; - if ( stream_.state == STREAM_CLOSED ) { - errorText_ = "RtApiCore::callbackEvent(): the stream is closed ... this shouldn't happen!"; - error( RtAudioError::WARNING ); - return FAILURE; - } - if ( stream_.bufferSize != nframes ) { - errorText_ = "RtApiCore::callbackEvent(): the JACK buffer size has changed ... cannot process!"; - error( RtAudioError::WARNING ); - return FAILURE; - } - - CallbackInfo *info = (CallbackInfo *) &stream_.callbackInfo; - JackHandle *handle = (JackHandle *) stream_.apiHandle; - - // Check if we were draining the stream and signal is finished. - if ( handle->drainCounter > 3 ) { - ThreadHandle threadId; - - stream_.state = STREAM_STOPPING; - if ( handle->internalDrain == true ) - pthread_create( &threadId, NULL, jackStopStream, info ); - else - pthread_cond_signal( &handle->condition ); - return SUCCESS; - } - - // Invoke user callback first, to get fresh output data. - if ( handle->drainCounter == 0 ) { - RtAudioCallback callback = (RtAudioCallback) info->callback; - double streamTime = getStreamTime(); - RtAudioStreamStatus status = 0; - if ( stream_.mode != INPUT && handle->xrun[0] == true ) { - status |= RTAUDIO_OUTPUT_UNDERFLOW; - handle->xrun[0] = false; - } - if ( stream_.mode != OUTPUT && handle->xrun[1] == true ) { - status |= RTAUDIO_INPUT_OVERFLOW; - handle->xrun[1] = false; - } - int cbReturnValue = callback( stream_.userBuffer[0], stream_.userBuffer[1], - stream_.bufferSize, streamTime, status, info->userData ); - if ( cbReturnValue == 2 ) { - stream_.state = STREAM_STOPPING; - handle->drainCounter = 2; - ThreadHandle id; - pthread_create( &id, NULL, jackStopStream, info ); - return SUCCESS; - } - else if ( cbReturnValue == 1 ) { - handle->drainCounter = 1; - handle->internalDrain = true; - } - } - - jack_default_audio_sample_t *jackbuffer; - unsigned long bufferBytes = nframes * sizeof( jack_default_audio_sample_t ); - if ( stream_.mode == OUTPUT || stream_.mode == DUPLEX ) { - - if ( handle->drainCounter > 1 ) { // write zeros to the output stream - - for ( unsigned int i=0; iports[0][i], (jack_nframes_t) nframes ); - memset( jackbuffer, 0, bufferBytes ); - } - - } - else if ( stream_.doConvertBuffer[0] ) { - - convertBuffer( stream_.deviceBuffer, stream_.userBuffer[0], stream_.convertInfo[0] ); - - for ( unsigned int i=0; iports[0][i], (jack_nframes_t) nframes ); - memcpy( jackbuffer, &stream_.deviceBuffer[i*bufferBytes], bufferBytes ); - } - } - else { // no buffer conversion - for ( unsigned int i=0; iports[0][i], (jack_nframes_t) nframes ); - memcpy( jackbuffer, &stream_.userBuffer[0][i*bufferBytes], bufferBytes ); - } - } - } - - // Don't bother draining input - if ( handle->drainCounter ) { - handle->drainCounter++; - goto unlock; - } - - if ( stream_.mode == INPUT || stream_.mode == DUPLEX ) { - - if ( stream_.doConvertBuffer[1] ) { - for ( unsigned int i=0; iports[1][i], (jack_nframes_t) nframes ); - memcpy( &stream_.deviceBuffer[i*bufferBytes], jackbuffer, bufferBytes ); - } - convertBuffer( stream_.userBuffer[1], stream_.deviceBuffer, stream_.convertInfo[1] ); - } - else { // no buffer conversion - for ( unsigned int i=0; iports[1][i], (jack_nframes_t) nframes ); - memcpy( &stream_.userBuffer[1][i*bufferBytes], jackbuffer, bufferBytes ); - } - } - } - - unlock: - RtApi::tickStreamTime(); - return SUCCESS; -} - //******************** End of __UNIX_JACK__ *********************// -#endif - -#if defined(__WINDOWS_ASIO__) // ASIO API on Windows - -// The ASIO API is designed around a callback scheme, so this -// implementation is similar to that used for OS-X CoreAudio and Linux -// Jack. The primary constraint with ASIO is that it only allows -// access to a single driver at a time. Thus, it is not possible to -// have more than one simultaneous RtAudio stream. -// -// This implementation also requires a number of external ASIO files -// and a few global variables. The ASIO callback scheme does not -// allow for the passing of user data, so we must create a global -// pointer to our callbackInfo structure. -// -// On unix systems, we make use of a pthread condition variable. -// Since there is no equivalent in Windows, I hacked something based -// on information found in -// http://www.cs.wustl.edu/~schmidt/win32-cv-1.html. - -#include "asiosys.h" -#include "asio.h" -#include "iasiothiscallresolver.h" -#include "asiodrivers.h" -#include - -static AsioDrivers drivers; -static ASIOCallbacks asioCallbacks; -static ASIODriverInfo driverInfo; -static CallbackInfo *asioCallbackInfo; -static bool asioXRun; - -struct AsioHandle { - int drainCounter; // Tracks callback counts when draining - bool internalDrain; // Indicates if stop is initiated from callback or not. - ASIOBufferInfo *bufferInfos; - HANDLE condition; - - AsioHandle() - :drainCounter(0), internalDrain(false), bufferInfos(0) {} -}; - -// Function declarations (definitions at end of section) -static const char* getAsioErrorString( ASIOError result ); -static void sampleRateChanged( ASIOSampleRate sRate ); -static long asioMessages( long selector, long value, void* message, double* opt ); - -RtApiAsio :: RtApiAsio() -{ - // ASIO cannot run on a multi-threaded appartment. You can call - // CoInitialize beforehand, but it must be for appartment threading - // (in which case, CoInitilialize will return S_FALSE here). - coInitialized_ = false; - HRESULT hr = CoInitialize( NULL ); - if ( FAILED(hr) ) { - errorText_ = "RtApiAsio::ASIO requires a single-threaded appartment. Call CoInitializeEx(0,COINIT_APARTMENTTHREADED)"; - error( RtAudioError::WARNING ); - } - coInitialized_ = true; - - drivers.removeCurrentDriver(); - driverInfo.asioVersion = 2; - - // See note in DirectSound implementation about GetDesktopWindow(). - driverInfo.sysRef = GetForegroundWindow(); -} - -RtApiAsio :: ~RtApiAsio() -{ - if ( stream_.state != STREAM_CLOSED ) closeStream(); - if ( coInitialized_ ) CoUninitialize(); -} - -unsigned int RtApiAsio :: getDeviceCount( void ) -{ - return (unsigned int) drivers.asioGetNumDev(); -} - -RtAudio::DeviceInfo RtApiAsio :: getDeviceInfo( unsigned int device ) -{ - RtAudio::DeviceInfo info; - info.probed = false; - - // Get device ID - unsigned int nDevices = getDeviceCount(); - if ( nDevices == 0 ) { - errorText_ = "RtApiAsio::getDeviceInfo: no devices found!"; - error( RtAudioError::INVALID_USE ); - return info; - } - - if ( device >= nDevices ) { - errorText_ = "RtApiAsio::getDeviceInfo: device ID is invalid!"; - error( RtAudioError::INVALID_USE ); - return info; - } - - // If a stream is already open, we cannot probe other devices. Thus, use the saved results. - if ( stream_.state != STREAM_CLOSED ) { - if ( device >= devices_.size() ) { - errorText_ = "RtApiAsio::getDeviceInfo: device ID was not present before stream was opened."; - error( RtAudioError::WARNING ); - return info; - } - return devices_[ device ]; - } - - char driverName[32]; - ASIOError result = drivers.asioGetDriverName( (int) device, driverName, 32 ); - if ( result != ASE_OK ) { - errorStream_ << "RtApiAsio::getDeviceInfo: unable to get driver name (" << getAsioErrorString( result ) << ")."; - errorText_ = errorStream_.str(); - error( RtAudioError::WARNING ); - return info; - } - - info.name = driverName; - - if ( !drivers.loadDriver( driverName ) ) { - errorStream_ << "RtApiAsio::getDeviceInfo: unable to load driver (" << driverName << ")."; - errorText_ = errorStream_.str(); - error( RtAudioError::WARNING ); - return info; - } - - result = ASIOInit( &driverInfo ); - if ( result != ASE_OK ) { - errorStream_ << "RtApiAsio::getDeviceInfo: error (" << getAsioErrorString( result ) << ") initializing driver (" << driverName << ")."; - errorText_ = errorStream_.str(); - error( RtAudioError::WARNING ); - return info; - } - - // Determine the device channel information. - long inputChannels, outputChannels; - result = ASIOGetChannels( &inputChannels, &outputChannels ); - if ( result != ASE_OK ) { - drivers.removeCurrentDriver(); - errorStream_ << "RtApiAsio::getDeviceInfo: error (" << getAsioErrorString( result ) << ") getting channel count (" << driverName << ")."; - errorText_ = errorStream_.str(); - error( RtAudioError::WARNING ); - return info; - } - - info.outputChannels = outputChannels; - info.inputChannels = inputChannels; - if ( info.outputChannels > 0 && info.inputChannels > 0 ) - info.duplexChannels = (info.outputChannels > info.inputChannels) ? info.inputChannels : info.outputChannels; - - // Determine the supported sample rates. - info.sampleRates.clear(); - for ( unsigned int i=0; i info.preferredSampleRate ) ) - info.preferredSampleRate = SAMPLE_RATES[i]; - } - } - - // Determine supported data types ... just check first channel and assume rest are the same. - ASIOChannelInfo channelInfo; - channelInfo.channel = 0; - channelInfo.isInput = true; - if ( info.inputChannels <= 0 ) channelInfo.isInput = false; - result = ASIOGetChannelInfo( &channelInfo ); - if ( result != ASE_OK ) { - drivers.removeCurrentDriver(); - errorStream_ << "RtApiAsio::getDeviceInfo: error (" << getAsioErrorString( result ) << ") getting driver channel info (" << driverName << ")."; - errorText_ = errorStream_.str(); - error( RtAudioError::WARNING ); - return info; - } - - info.nativeFormats = 0; - if ( channelInfo.type == ASIOSTInt16MSB || channelInfo.type == ASIOSTInt16LSB ) - info.nativeFormats |= RTAUDIO_SINT16; - else if ( channelInfo.type == ASIOSTInt32MSB || channelInfo.type == ASIOSTInt32LSB ) - info.nativeFormats |= RTAUDIO_SINT32; - else if ( channelInfo.type == ASIOSTFloat32MSB || channelInfo.type == ASIOSTFloat32LSB ) - info.nativeFormats |= RTAUDIO_FLOAT32; - else if ( channelInfo.type == ASIOSTFloat64MSB || channelInfo.type == ASIOSTFloat64LSB ) - info.nativeFormats |= RTAUDIO_FLOAT64; - else if ( channelInfo.type == ASIOSTInt24MSB || channelInfo.type == ASIOSTInt24LSB ) - info.nativeFormats |= RTAUDIO_SINT24; - - if ( info.outputChannels > 0 ) - if ( getDefaultOutputDevice() == device ) info.isDefaultOutput = true; - if ( info.inputChannels > 0 ) - if ( getDefaultInputDevice() == device ) info.isDefaultInput = true; - - info.probed = true; - drivers.removeCurrentDriver(); - return info; -} - -static void bufferSwitch( long index, ASIOBool /*processNow*/ ) -{ - RtApiAsio *object = (RtApiAsio *) asioCallbackInfo->object; - object->callbackEvent( index ); -} - -void RtApiAsio :: saveDeviceInfo( void ) -{ - devices_.clear(); - - unsigned int nDevices = getDeviceCount(); - devices_.resize( nDevices ); - for ( unsigned int i=0; isaveDeviceInfo(); - - if ( !drivers.loadDriver( driverName ) ) { - errorStream_ << "RtApiAsio::probeDeviceOpen: unable to load driver (" << driverName << ")."; - errorText_ = errorStream_.str(); - return FAILURE; - } - - result = ASIOInit( &driverInfo ); - if ( result != ASE_OK ) { - errorStream_ << "RtApiAsio::probeDeviceOpen: error (" << getAsioErrorString( result ) << ") initializing driver (" << driverName << ")."; - errorText_ = errorStream_.str(); - return FAILURE; - } - } - - // keep them before any "goto error", they are used for error cleanup + goto device boundary checks - bool buffersAllocated = false; - AsioHandle *handle = (AsioHandle *) stream_.apiHandle; - unsigned int nChannels; - - - // Check the device channel count. - long inputChannels, outputChannels; - result = ASIOGetChannels( &inputChannels, &outputChannels ); - if ( result != ASE_OK ) { - errorStream_ << "RtApiAsio::probeDeviceOpen: error (" << getAsioErrorString( result ) << ") getting channel count (" << driverName << ")."; - errorText_ = errorStream_.str(); - goto error; - } - - if ( ( mode == OUTPUT && (channels+firstChannel) > (unsigned int) outputChannels) || - ( mode == INPUT && (channels+firstChannel) > (unsigned int) inputChannels) ) { - errorStream_ << "RtApiAsio::probeDeviceOpen: driver (" << driverName << ") does not support requested channel count (" << channels << ") + offset (" << firstChannel << ")."; - errorText_ = errorStream_.str(); - goto error; - } - stream_.nDeviceChannels[mode] = channels; - stream_.nUserChannels[mode] = channels; - stream_.channelOffset[mode] = firstChannel; - - // Verify the sample rate is supported. - result = ASIOCanSampleRate( (ASIOSampleRate) sampleRate ); - if ( result != ASE_OK ) { - errorStream_ << "RtApiAsio::probeDeviceOpen: driver (" << driverName << ") does not support requested sample rate (" << sampleRate << ")."; - errorText_ = errorStream_.str(); - goto error; - } - - // Get the current sample rate - ASIOSampleRate currentRate; - result = ASIOGetSampleRate( ¤tRate ); - if ( result != ASE_OK ) { - errorStream_ << "RtApiAsio::probeDeviceOpen: driver (" << driverName << ") error getting sample rate."; - errorText_ = errorStream_.str(); - goto error; - } - - // Set the sample rate only if necessary - if ( currentRate != sampleRate ) { - result = ASIOSetSampleRate( (ASIOSampleRate) sampleRate ); - if ( result != ASE_OK ) { - errorStream_ << "RtApiAsio::probeDeviceOpen: driver (" << driverName << ") error setting sample rate (" << sampleRate << ")."; - errorText_ = errorStream_.str(); - goto error; - } - } - - // Determine the driver data type. - ASIOChannelInfo channelInfo; - channelInfo.channel = 0; - if ( mode == OUTPUT ) channelInfo.isInput = false; - else channelInfo.isInput = true; - result = ASIOGetChannelInfo( &channelInfo ); - if ( result != ASE_OK ) { - errorStream_ << "RtApiAsio::probeDeviceOpen: driver (" << driverName << ") error (" << getAsioErrorString( result ) << ") getting data format."; - errorText_ = errorStream_.str(); - goto error; - } - - // Assuming WINDOWS host is always little-endian. - stream_.doByteSwap[mode] = false; - stream_.userFormat = format; - stream_.deviceFormat[mode] = 0; - if ( channelInfo.type == ASIOSTInt16MSB || channelInfo.type == ASIOSTInt16LSB ) { - stream_.deviceFormat[mode] = RTAUDIO_SINT16; - if ( channelInfo.type == ASIOSTInt16MSB ) stream_.doByteSwap[mode] = true; - } - else if ( channelInfo.type == ASIOSTInt32MSB || channelInfo.type == ASIOSTInt32LSB ) { - stream_.deviceFormat[mode] = RTAUDIO_SINT32; - if ( channelInfo.type == ASIOSTInt32MSB ) stream_.doByteSwap[mode] = true; - } - else if ( channelInfo.type == ASIOSTFloat32MSB || channelInfo.type == ASIOSTFloat32LSB ) { - stream_.deviceFormat[mode] = RTAUDIO_FLOAT32; - if ( channelInfo.type == ASIOSTFloat32MSB ) stream_.doByteSwap[mode] = true; - } - else if ( channelInfo.type == ASIOSTFloat64MSB || channelInfo.type == ASIOSTFloat64LSB ) { - stream_.deviceFormat[mode] = RTAUDIO_FLOAT64; - if ( channelInfo.type == ASIOSTFloat64MSB ) stream_.doByteSwap[mode] = true; - } - else if ( channelInfo.type == ASIOSTInt24MSB || channelInfo.type == ASIOSTInt24LSB ) { - stream_.deviceFormat[mode] = RTAUDIO_SINT24; - if ( channelInfo.type == ASIOSTInt24MSB ) stream_.doByteSwap[mode] = true; - } - - if ( stream_.deviceFormat[mode] == 0 ) { - errorStream_ << "RtApiAsio::probeDeviceOpen: driver (" << driverName << ") data format not supported by RtAudio."; - errorText_ = errorStream_.str(); - goto error; - } - - // Set the buffer size. For a duplex stream, this will end up - // setting the buffer size based on the input constraints, which - // should be ok. - long minSize, maxSize, preferSize, granularity; - result = ASIOGetBufferSize( &minSize, &maxSize, &preferSize, &granularity ); - if ( result != ASE_OK ) { - errorStream_ << "RtApiAsio::probeDeviceOpen: driver (" << driverName << ") error (" << getAsioErrorString( result ) << ") getting buffer size."; - errorText_ = errorStream_.str(); - goto error; - } - - if ( isDuplexInput ) { - // When this is the duplex input (output was opened before), then we have to use the same - // buffersize as the output, because it might use the preferred buffer size, which most - // likely wasn't passed as input to this. The buffer sizes have to be identically anyway, - // So instead of throwing an error, make them equal. The caller uses the reference - // to the "bufferSize" param as usual to set up processing buffers. - - *bufferSize = stream_.bufferSize; - - } else { - if ( *bufferSize == 0 ) *bufferSize = preferSize; - else if ( *bufferSize < (unsigned int) minSize ) *bufferSize = (unsigned int) minSize; - else if ( *bufferSize > (unsigned int) maxSize ) *bufferSize = (unsigned int) maxSize; - else if ( granularity == -1 ) { - // Make sure bufferSize is a power of two. - int log2_of_min_size = 0; - int log2_of_max_size = 0; - - for ( unsigned int i = 0; i < sizeof(long) * 8; i++ ) { - if ( minSize & ((long)1 << i) ) log2_of_min_size = i; - if ( maxSize & ((long)1 << i) ) log2_of_max_size = i; - } - - long min_delta = std::abs( (long)*bufferSize - ((long)1 << log2_of_min_size) ); - int min_delta_num = log2_of_min_size; - - for (int i = log2_of_min_size + 1; i <= log2_of_max_size; i++) { - long current_delta = std::abs( (long)*bufferSize - ((long)1 << i) ); - if (current_delta < min_delta) { - min_delta = current_delta; - min_delta_num = i; - } - } - - *bufferSize = ( (unsigned int)1 << min_delta_num ); - if ( *bufferSize < (unsigned int) minSize ) *bufferSize = (unsigned int) minSize; - else if ( *bufferSize > (unsigned int) maxSize ) *bufferSize = (unsigned int) maxSize; - } - else if ( granularity != 0 ) { - // Set to an even multiple of granularity, rounding up. - *bufferSize = (*bufferSize + granularity-1) / granularity * granularity; - } - } - - /* - // we don't use it anymore, see above! - // Just left it here for the case... - if ( isDuplexInput && stream_.bufferSize != *bufferSize ) { - errorText_ = "RtApiAsio::probeDeviceOpen: input/output buffersize discrepancy!"; - goto error; - } - */ - - stream_.bufferSize = *bufferSize; - stream_.nBuffers = 2; - - if ( options && options->flags & RTAUDIO_NONINTERLEAVED ) stream_.userInterleaved = false; - else stream_.userInterleaved = true; - - // ASIO always uses non-interleaved buffers. - stream_.deviceInterleaved[mode] = false; - - // Allocate, if necessary, our AsioHandle structure for the stream. - if ( handle == 0 ) { - try { - handle = new AsioHandle; - } - catch ( std::bad_alloc& ) { - errorText_ = "RtApiAsio::probeDeviceOpen: error allocating AsioHandle memory."; - goto error; - } - handle->bufferInfos = 0; - - // Create a manual-reset event. - handle->condition = CreateEvent( NULL, // no security - TRUE, // manual-reset - FALSE, // non-signaled initially - NULL ); // unnamed - stream_.apiHandle = (void *) handle; - } - - // Create the ASIO internal buffers. Since RtAudio sets up input - // and output separately, we'll have to dispose of previously - // created output buffers for a duplex stream. - if ( mode == INPUT && stream_.mode == OUTPUT ) { - ASIODisposeBuffers(); - if ( handle->bufferInfos ) free( handle->bufferInfos ); - } - - // Allocate, initialize, and save the bufferInfos in our stream callbackInfo structure. - unsigned int i; - nChannels = stream_.nDeviceChannels[0] + stream_.nDeviceChannels[1]; - handle->bufferInfos = (ASIOBufferInfo *) malloc( nChannels * sizeof(ASIOBufferInfo) ); - if ( handle->bufferInfos == NULL ) { - errorStream_ << "RtApiAsio::probeDeviceOpen: error allocating bufferInfo memory for driver (" << driverName << ")."; - errorText_ = errorStream_.str(); - goto error; - } - - ASIOBufferInfo *infos; - infos = handle->bufferInfos; - for ( i=0; iisInput = ASIOFalse; - infos->channelNum = i + stream_.channelOffset[0]; - infos->buffers[0] = infos->buffers[1] = 0; - } - for ( i=0; iisInput = ASIOTrue; - infos->channelNum = i + stream_.channelOffset[1]; - infos->buffers[0] = infos->buffers[1] = 0; - } - - // prepare for callbacks - stream_.sampleRate = sampleRate; - stream_.device[mode] = device; - stream_.mode = isDuplexInput ? DUPLEX : mode; - - // store this class instance before registering callbacks, that are going to use it - asioCallbackInfo = &stream_.callbackInfo; - stream_.callbackInfo.object = (void *) this; - - // Set up the ASIO callback structure and create the ASIO data buffers. - asioCallbacks.bufferSwitch = &bufferSwitch; - asioCallbacks.sampleRateDidChange = &sampleRateChanged; - asioCallbacks.asioMessage = &asioMessages; - asioCallbacks.bufferSwitchTimeInfo = NULL; - result = ASIOCreateBuffers( handle->bufferInfos, nChannels, stream_.bufferSize, &asioCallbacks ); - if ( result != ASE_OK ) { - // Standard method failed. This can happen with strict/misbehaving drivers that return valid buffer size ranges - // but only accept the preferred buffer size as parameter for ASIOCreateBuffers (e.g. Creative's ASIO driver). - // In that case, let's be naïve and try that instead. - *bufferSize = preferSize; - stream_.bufferSize = *bufferSize; - result = ASIOCreateBuffers( handle->bufferInfos, nChannels, stream_.bufferSize, &asioCallbacks ); - } - - if ( result != ASE_OK ) { - errorStream_ << "RtApiAsio::probeDeviceOpen: driver (" << driverName << ") error (" << getAsioErrorString( result ) << ") creating buffers."; - errorText_ = errorStream_.str(); - goto error; - } - buffersAllocated = true; - stream_.state = STREAM_STOPPED; - - // Set flags for buffer conversion. - stream_.doConvertBuffer[mode] = false; - if ( stream_.userFormat != stream_.deviceFormat[mode] ) - stream_.doConvertBuffer[mode] = true; - if ( stream_.userInterleaved != stream_.deviceInterleaved[mode] && - stream_.nUserChannels[mode] > 1 ) - stream_.doConvertBuffer[mode] = true; - - // Allocate necessary internal buffers - unsigned long bufferBytes; - bufferBytes = stream_.nUserChannels[mode] * *bufferSize * formatBytes( stream_.userFormat ); - stream_.userBuffer[mode] = (char *) calloc( bufferBytes, 1 ); - if ( stream_.userBuffer[mode] == NULL ) { - errorText_ = "RtApiAsio::probeDeviceOpen: error allocating user buffer memory."; - goto error; - } - - if ( stream_.doConvertBuffer[mode] ) { - - bool makeBuffer = true; - bufferBytes = stream_.nDeviceChannels[mode] * formatBytes( stream_.deviceFormat[mode] ); - if ( isDuplexInput && stream_.deviceBuffer ) { - unsigned long bytesOut = stream_.nDeviceChannels[0] * formatBytes( stream_.deviceFormat[0] ); - if ( bufferBytes <= bytesOut ) makeBuffer = false; - } - - if ( makeBuffer ) { - bufferBytes *= *bufferSize; - if ( stream_.deviceBuffer ) free( stream_.deviceBuffer ); - stream_.deviceBuffer = (char *) calloc( bufferBytes, 1 ); - if ( stream_.deviceBuffer == NULL ) { - errorText_ = "RtApiAsio::probeDeviceOpen: error allocating device buffer memory."; - goto error; - } - } - } - - // Determine device latencies - long inputLatency, outputLatency; - result = ASIOGetLatencies( &inputLatency, &outputLatency ); - if ( result != ASE_OK ) { - errorStream_ << "RtApiAsio::probeDeviceOpen: driver (" << driverName << ") error (" << getAsioErrorString( result ) << ") getting latency."; - errorText_ = errorStream_.str(); - error( RtAudioError::WARNING); // warn but don't fail - } - else { - stream_.latency[0] = outputLatency; - stream_.latency[1] = inputLatency; - } - - // Setup the buffer conversion information structure. We don't use - // buffers to do channel offsets, so we override that parameter - // here. - if ( stream_.doConvertBuffer[mode] ) setConvertInfo( mode, 0 ); - - return SUCCESS; - - error: - if ( !isDuplexInput ) { - // the cleanup for error in the duplex input, is done by RtApi::openStream - // So we clean up for single channel only - - if ( buffersAllocated ) - ASIODisposeBuffers(); - - drivers.removeCurrentDriver(); - - if ( handle ) { - CloseHandle( handle->condition ); - if ( handle->bufferInfos ) - free( handle->bufferInfos ); - - delete handle; - stream_.apiHandle = 0; - } - - - if ( stream_.userBuffer[mode] ) { - free( stream_.userBuffer[mode] ); - stream_.userBuffer[mode] = 0; - } - - if ( stream_.deviceBuffer ) { - free( stream_.deviceBuffer ); - stream_.deviceBuffer = 0; - } - } - - return FAILURE; -}//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - -void RtApiAsio :: closeStream() -{ - if ( stream_.state == STREAM_CLOSED ) { - errorText_ = "RtApiAsio::closeStream(): no open stream to close!"; - error( RtAudioError::WARNING ); - return; - } - - if ( stream_.state == STREAM_RUNNING ) { - stream_.state = STREAM_STOPPED; - ASIOStop(); - } - ASIODisposeBuffers(); - drivers.removeCurrentDriver(); - - AsioHandle *handle = (AsioHandle *) stream_.apiHandle; - if ( handle ) { - CloseHandle( handle->condition ); - if ( handle->bufferInfos ) - free( handle->bufferInfos ); - delete handle; - stream_.apiHandle = 0; - } - - for ( int i=0; i<2; i++ ) { - if ( stream_.userBuffer[i] ) { - free( stream_.userBuffer[i] ); - stream_.userBuffer[i] = 0; - } - } - - if ( stream_.deviceBuffer ) { - free( stream_.deviceBuffer ); - stream_.deviceBuffer = 0; - } - - stream_.mode = UNINITIALIZED; - stream_.state = STREAM_CLOSED; -} - -bool stopThreadCalled = false; - -void RtApiAsio :: startStream() -{ - verifyStream(); - if ( stream_.state == STREAM_RUNNING ) { - errorText_ = "RtApiAsio::startStream(): the stream is already running!"; - error( RtAudioError::WARNING ); - return; - } - - #if defined( HAVE_GETTIMEOFDAY ) - gettimeofday( &stream_.lastTickTimestamp, NULL ); - #endif - - AsioHandle *handle = (AsioHandle *) stream_.apiHandle; - ASIOError result = ASIOStart(); - if ( result != ASE_OK ) { - errorStream_ << "RtApiAsio::startStream: error (" << getAsioErrorString( result ) << ") starting device."; - errorText_ = errorStream_.str(); - goto unlock; - } - - handle->drainCounter = 0; - handle->internalDrain = false; - ResetEvent( handle->condition ); - stream_.state = STREAM_RUNNING; - asioXRun = false; - - unlock: - stopThreadCalled = false; - - if ( result == ASE_OK ) return; - error( RtAudioError::SYSTEM_ERROR ); -} - -void RtApiAsio :: stopStream() -{ - verifyStream(); - if ( stream_.state == STREAM_STOPPED ) { - errorText_ = "RtApiAsio::stopStream(): the stream is already stopped!"; - error( RtAudioError::WARNING ); - return; - } - - AsioHandle *handle = (AsioHandle *) stream_.apiHandle; - if ( stream_.mode == OUTPUT || stream_.mode == DUPLEX ) { - if ( handle->drainCounter == 0 ) { - handle->drainCounter = 2; - WaitForSingleObject( handle->condition, INFINITE ); // block until signaled - } - } - - stream_.state = STREAM_STOPPED; - - ASIOError result = ASIOStop(); - if ( result != ASE_OK ) { - errorStream_ << "RtApiAsio::stopStream: error (" << getAsioErrorString( result ) << ") stopping device."; - errorText_ = errorStream_.str(); - } - - if ( result == ASE_OK ) return; - error( RtAudioError::SYSTEM_ERROR ); -} - -void RtApiAsio :: abortStream() -{ - verifyStream(); - if ( stream_.state == STREAM_STOPPED ) { - errorText_ = "RtApiAsio::abortStream(): the stream is already stopped!"; - error( RtAudioError::WARNING ); - return; - } - - // The following lines were commented-out because some behavior was - // noted where the device buffers need to be zeroed to avoid - // continuing sound, even when the device buffers are completely - // disposed. So now, calling abort is the same as calling stop. - // AsioHandle *handle = (AsioHandle *) stream_.apiHandle; - // handle->drainCounter = 2; - stopStream(); -} - -// This function will be called by a spawned thread when the user -// callback function signals that the stream should be stopped or -// aborted. It is necessary to handle it this way because the -// callbackEvent() function must return before the ASIOStop() -// function will return. -static unsigned __stdcall asioStopStream( void *ptr ) -{ - CallbackInfo *info = (CallbackInfo *) ptr; - RtApiAsio *object = (RtApiAsio *) info->object; - - object->stopStream(); - _endthreadex( 0 ); - return 0; -} - -bool RtApiAsio :: callbackEvent( long bufferIndex ) -{ - if ( stream_.state == STREAM_STOPPED || stream_.state == STREAM_STOPPING ) return SUCCESS; - if ( stream_.state == STREAM_CLOSED ) { - errorText_ = "RtApiAsio::callbackEvent(): the stream is closed ... this shouldn't happen!"; - error( RtAudioError::WARNING ); - return FAILURE; - } - - CallbackInfo *info = (CallbackInfo *) &stream_.callbackInfo; - AsioHandle *handle = (AsioHandle *) stream_.apiHandle; - - // Check if we were draining the stream and signal if finished. - if ( handle->drainCounter > 3 ) { - - stream_.state = STREAM_STOPPING; - if ( handle->internalDrain == false ) - SetEvent( handle->condition ); - else { // spawn a thread to stop the stream - unsigned threadId; - stream_.callbackInfo.thread = _beginthreadex( NULL, 0, &asioStopStream, - &stream_.callbackInfo, 0, &threadId ); - } - return SUCCESS; - } - - // Invoke user callback to get fresh output data UNLESS we are - // draining stream. - if ( handle->drainCounter == 0 ) { - RtAudioCallback callback = (RtAudioCallback) info->callback; - double streamTime = getStreamTime(); - RtAudioStreamStatus status = 0; - if ( stream_.mode != INPUT && asioXRun == true ) { - status |= RTAUDIO_OUTPUT_UNDERFLOW; - asioXRun = false; - } - if ( stream_.mode != OUTPUT && asioXRun == true ) { - status |= RTAUDIO_INPUT_OVERFLOW; - asioXRun = false; - } - int cbReturnValue = callback( stream_.userBuffer[0], stream_.userBuffer[1], - stream_.bufferSize, streamTime, status, info->userData ); - if ( cbReturnValue == 2 ) { - stream_.state = STREAM_STOPPING; - handle->drainCounter = 2; - unsigned threadId; - stream_.callbackInfo.thread = _beginthreadex( NULL, 0, &asioStopStream, - &stream_.callbackInfo, 0, &threadId ); - return SUCCESS; - } - else if ( cbReturnValue == 1 ) { - handle->drainCounter = 1; - handle->internalDrain = true; - } - } - - unsigned int nChannels, bufferBytes, i, j; - nChannels = stream_.nDeviceChannels[0] + stream_.nDeviceChannels[1]; - if ( stream_.mode == OUTPUT || stream_.mode == DUPLEX ) { - - bufferBytes = stream_.bufferSize * formatBytes( stream_.deviceFormat[0] ); - - if ( handle->drainCounter > 1 ) { // write zeros to the output stream - - for ( i=0, j=0; ibufferInfos[i].isInput != ASIOTrue ) - memset( handle->bufferInfos[i].buffers[bufferIndex], 0, bufferBytes ); - } - - } - else if ( stream_.doConvertBuffer[0] ) { - - convertBuffer( stream_.deviceBuffer, stream_.userBuffer[0], stream_.convertInfo[0] ); - if ( stream_.doByteSwap[0] ) - byteSwapBuffer( stream_.deviceBuffer, - stream_.bufferSize * stream_.nDeviceChannels[0], - stream_.deviceFormat[0] ); - - for ( i=0, j=0; ibufferInfos[i].isInput != ASIOTrue ) - memcpy( handle->bufferInfos[i].buffers[bufferIndex], - &stream_.deviceBuffer[j++*bufferBytes], bufferBytes ); - } - - } - else { - - if ( stream_.doByteSwap[0] ) - byteSwapBuffer( stream_.userBuffer[0], - stream_.bufferSize * stream_.nUserChannels[0], - stream_.userFormat ); - - for ( i=0, j=0; ibufferInfos[i].isInput != ASIOTrue ) - memcpy( handle->bufferInfos[i].buffers[bufferIndex], - &stream_.userBuffer[0][bufferBytes*j++], bufferBytes ); - } - - } - } - - // Don't bother draining input - if ( handle->drainCounter ) { - handle->drainCounter++; - goto unlock; - } - - if ( stream_.mode == INPUT || stream_.mode == DUPLEX ) { - - bufferBytes = stream_.bufferSize * formatBytes(stream_.deviceFormat[1]); - - if (stream_.doConvertBuffer[1]) { - - // Always interleave ASIO input data. - for ( i=0, j=0; ibufferInfos[i].isInput == ASIOTrue ) - memcpy( &stream_.deviceBuffer[j++*bufferBytes], - handle->bufferInfos[i].buffers[bufferIndex], - bufferBytes ); - } - - if ( stream_.doByteSwap[1] ) - byteSwapBuffer( stream_.deviceBuffer, - stream_.bufferSize * stream_.nDeviceChannels[1], - stream_.deviceFormat[1] ); - convertBuffer( stream_.userBuffer[1], stream_.deviceBuffer, stream_.convertInfo[1] ); - - } - else { - for ( i=0, j=0; ibufferInfos[i].isInput == ASIOTrue ) { - memcpy( &stream_.userBuffer[1][bufferBytes*j++], - handle->bufferInfos[i].buffers[bufferIndex], - bufferBytes ); - } - } - - if ( stream_.doByteSwap[1] ) - byteSwapBuffer( stream_.userBuffer[1], - stream_.bufferSize * stream_.nUserChannels[1], - stream_.userFormat ); - } - } - - unlock: - // The following call was suggested by Malte Clasen. While the API - // documentation indicates it should not be required, some device - // drivers apparently do not function correctly without it. - ASIOOutputReady(); - - RtApi::tickStreamTime(); - return SUCCESS; -} - -static void sampleRateChanged( ASIOSampleRate sRate ) -{ - // The ASIO documentation says that this usually only happens during - // external sync. Audio processing is not stopped by the driver, - // actual sample rate might not have even changed, maybe only the - // sample rate status of an AES/EBU or S/PDIF digital input at the - // audio device. - - RtApi *object = (RtApi *) asioCallbackInfo->object; - try { - object->stopStream(); - } - catch ( RtAudioError &exception ) { - std::cerr << "\nRtApiAsio: sampleRateChanged() error (" << exception.getMessage() << ")!\n" << std::endl; - return; - } - - std::cerr << "\nRtApiAsio: driver reports sample rate changed to " << sRate << " ... stream stopped!!!\n" << std::endl; -} - -static long asioMessages( long selector, long value, void* /*message*/, double* /*opt*/ ) -{ - long ret = 0; - - switch( selector ) { - case kAsioSelectorSupported: - if ( value == kAsioResetRequest - || value == kAsioEngineVersion - || value == kAsioResyncRequest - || value == kAsioLatenciesChanged - // The following three were added for ASIO 2.0, you don't - // necessarily have to support them. - || value == kAsioSupportsTimeInfo - || value == kAsioSupportsTimeCode - || value == kAsioSupportsInputMonitor) - ret = 1L; - break; - case kAsioResetRequest: - // Defer the task and perform the reset of the driver during the - // next "safe" situation. You cannot reset the driver right now, - // as this code is called from the driver. Reset the driver is - // done by completely destruct is. I.e. ASIOStop(), - // ASIODisposeBuffers(), Destruction Afterwards you initialize the - // driver again. - std::cerr << "\nRtApiAsio: driver reset requested!!!" << std::endl; - ret = 1L; - break; - case kAsioResyncRequest: - // This informs the application that the driver encountered some - // non-fatal data loss. It is used for synchronization purposes - // of different media. Added mainly to work around the Win16Mutex - // problems in Windows 95/98 with the Windows Multimedia system, - // which could lose data because the Mutex was held too long by - // another thread. However a driver can issue it in other - // situations, too. - // std::cerr << "\nRtApiAsio: driver resync requested!!!" << std::endl; - asioXRun = true; - ret = 1L; - break; - case kAsioLatenciesChanged: - // This will inform the host application that the drivers were - // latencies changed. Beware, it this does not mean that the - // buffer sizes have changed! You might need to update internal - // delay data. - std::cerr << "\nRtApiAsio: driver latency may have changed!!!" << std::endl; - ret = 1L; - break; - case kAsioEngineVersion: - // Return the supported ASIO version of the host application. If - // a host application does not implement this selector, ASIO 1.0 - // is assumed by the driver. - ret = 2L; - break; - case kAsioSupportsTimeInfo: - // Informs the driver whether the - // asioCallbacks.bufferSwitchTimeInfo() callback is supported. - // For compatibility with ASIO 1.0 drivers the host application - // should always support the "old" bufferSwitch method, too. - ret = 0; - break; - case kAsioSupportsTimeCode: - // Informs the driver whether application is interested in time - // code info. If an application does not need to know about time - // code, the driver has less work to do. - ret = 0; - break; - } - return ret; -} - -static const char* getAsioErrorString( ASIOError result ) -{ - struct Messages - { - ASIOError value; - const char*message; - }; - - static const Messages m[] = - { - { ASE_NotPresent, "Hardware input or output is not present or available." }, - { ASE_HWMalfunction, "Hardware is malfunctioning." }, - { ASE_InvalidParameter, "Invalid input parameter." }, - { ASE_InvalidMode, "Invalid mode." }, - { ASE_SPNotAdvancing, "Sample position not advancing." }, - { ASE_NoClock, "Sample clock or rate cannot be determined or is not present." }, - { ASE_NoMemory, "Not enough memory to complete the request." } - }; - - for ( unsigned int i = 0; i < sizeof(m)/sizeof(m[0]); ++i ) - if ( m[i].value == result ) return m[i].message; - - return "Unknown error."; -} - -//******************** End of __WINDOWS_ASIO__ *********************// -#endif - - -#if defined(__WINDOWS_WASAPI__) // Windows WASAPI API - -// Authored by Marcus Tomlinson , April 2014 -// - Introduces support for the Windows WASAPI API -// - Aims to deliver bit streams to and from hardware at the lowest possible latency, via the absolute minimum buffer sizes required -// - Provides flexible stream configuration to an otherwise strict and inflexible WASAPI interface -// - Includes automatic internal conversion of sample rate and buffer size between hardware and the user - -#ifndef INITGUID - #define INITGUID -#endif - -#include -#include -#include -#include -#include - -#include -#include -#include -#include - -#ifndef MF_E_TRANSFORM_NEED_MORE_INPUT - #define MF_E_TRANSFORM_NEED_MORE_INPUT _HRESULT_TYPEDEF_(0xc00d6d72) -#endif - -#ifndef MFSTARTUP_NOSOCKET - #define MFSTARTUP_NOSOCKET 0x1 -#endif - -#ifdef _MSC_VER - #pragma comment( lib, "ksuser" ) - #pragma comment( lib, "mfplat.lib" ) - #pragma comment( lib, "mfuuid.lib" ) - #pragma comment( lib, "wmcodecdspuuid" ) -#endif - -//============================================================================= - -#define SAFE_RELEASE( objectPtr )\ -if ( objectPtr )\ -{\ - objectPtr->Release();\ - objectPtr = NULL;\ -} - -typedef HANDLE ( __stdcall *TAvSetMmThreadCharacteristicsPtr )( LPCWSTR TaskName, LPDWORD TaskIndex ); - -//----------------------------------------------------------------------------- - -// WASAPI dictates stream sample rate, format, channel count, and in some cases, buffer size. -// Therefore we must perform all necessary conversions to user buffers in order to satisfy these -// requirements. WasapiBuffer ring buffers are used between HwIn->UserIn and UserOut->HwOut to -// provide intermediate storage for read / write synchronization. -class WasapiBuffer -{ -public: - WasapiBuffer() - : buffer_( NULL ), - bufferSize_( 0 ), - inIndex_( 0 ), - outIndex_( 0 ) {} - - ~WasapiBuffer() { - free( buffer_ ); - } - - // sets the length of the internal ring buffer - void setBufferSize( unsigned int bufferSize, unsigned int formatBytes ) { - free( buffer_ ); - - buffer_ = ( char* ) calloc( bufferSize, formatBytes ); - - bufferSize_ = bufferSize; - inIndex_ = 0; - outIndex_ = 0; - } - - // attempt to push a buffer into the ring buffer at the current "in" index - bool pushBuffer( char* buffer, unsigned int bufferSize, RtAudioFormat format ) - { - if ( !buffer || // incoming buffer is NULL - bufferSize == 0 || // incoming buffer has no data - bufferSize > bufferSize_ ) // incoming buffer too large - { - return false; - } - - unsigned int relOutIndex = outIndex_; - unsigned int inIndexEnd = inIndex_ + bufferSize; - if ( relOutIndex < inIndex_ && inIndexEnd >= bufferSize_ ) { - relOutIndex += bufferSize_; - } - - // the "IN" index CAN BEGIN at the "OUT" index - // the "IN" index CANNOT END at the "OUT" index - if ( inIndex_ < relOutIndex && inIndexEnd >= relOutIndex ) { - return false; // not enough space between "in" index and "out" index - } - - // copy buffer from external to internal - int fromZeroSize = inIndex_ + bufferSize - bufferSize_; - fromZeroSize = fromZeroSize < 0 ? 0 : fromZeroSize; - int fromInSize = bufferSize - fromZeroSize; - - switch( format ) - { - case RTAUDIO_SINT8: - memcpy( &( ( char* ) buffer_ )[inIndex_], buffer, fromInSize * sizeof( char ) ); - memcpy( buffer_, &( ( char* ) buffer )[fromInSize], fromZeroSize * sizeof( char ) ); - break; - case RTAUDIO_SINT16: - memcpy( &( ( short* ) buffer_ )[inIndex_], buffer, fromInSize * sizeof( short ) ); - memcpy( buffer_, &( ( short* ) buffer )[fromInSize], fromZeroSize * sizeof( short ) ); - break; - case RTAUDIO_SINT24: - memcpy( &( ( S24* ) buffer_ )[inIndex_], buffer, fromInSize * sizeof( S24 ) ); - memcpy( buffer_, &( ( S24* ) buffer )[fromInSize], fromZeroSize * sizeof( S24 ) ); - break; - case RTAUDIO_SINT32: - memcpy( &( ( int* ) buffer_ )[inIndex_], buffer, fromInSize * sizeof( int ) ); - memcpy( buffer_, &( ( int* ) buffer )[fromInSize], fromZeroSize * sizeof( int ) ); - break; - case RTAUDIO_FLOAT32: - memcpy( &( ( float* ) buffer_ )[inIndex_], buffer, fromInSize * sizeof( float ) ); - memcpy( buffer_, &( ( float* ) buffer )[fromInSize], fromZeroSize * sizeof( float ) ); - break; - case RTAUDIO_FLOAT64: - memcpy( &( ( double* ) buffer_ )[inIndex_], buffer, fromInSize * sizeof( double ) ); - memcpy( buffer_, &( ( double* ) buffer )[fromInSize], fromZeroSize * sizeof( double ) ); - break; - } - - // update "in" index - inIndex_ += bufferSize; - inIndex_ %= bufferSize_; - - return true; - } - - // attempt to pull a buffer from the ring buffer from the current "out" index - bool pullBuffer( char* buffer, unsigned int bufferSize, RtAudioFormat format ) - { - if ( !buffer || // incoming buffer is NULL - bufferSize == 0 || // incoming buffer has no data - bufferSize > bufferSize_ ) // incoming buffer too large - { - return false; - } - - unsigned int relInIndex = inIndex_; - unsigned int outIndexEnd = outIndex_ + bufferSize; - if ( relInIndex < outIndex_ && outIndexEnd >= bufferSize_ ) { - relInIndex += bufferSize_; - } - - // the "OUT" index CANNOT BEGIN at the "IN" index - // the "OUT" index CAN END at the "IN" index - if ( outIndex_ <= relInIndex && outIndexEnd > relInIndex ) { - return false; // not enough space between "out" index and "in" index - } - - // copy buffer from internal to external - int fromZeroSize = outIndex_ + bufferSize - bufferSize_; - fromZeroSize = fromZeroSize < 0 ? 0 : fromZeroSize; - int fromOutSize = bufferSize - fromZeroSize; - - switch( format ) - { - case RTAUDIO_SINT8: - memcpy( buffer, &( ( char* ) buffer_ )[outIndex_], fromOutSize * sizeof( char ) ); - memcpy( &( ( char* ) buffer )[fromOutSize], buffer_, fromZeroSize * sizeof( char ) ); - break; - case RTAUDIO_SINT16: - memcpy( buffer, &( ( short* ) buffer_ )[outIndex_], fromOutSize * sizeof( short ) ); - memcpy( &( ( short* ) buffer )[fromOutSize], buffer_, fromZeroSize * sizeof( short ) ); - break; - case RTAUDIO_SINT24: - memcpy( buffer, &( ( S24* ) buffer_ )[outIndex_], fromOutSize * sizeof( S24 ) ); - memcpy( &( ( S24* ) buffer )[fromOutSize], buffer_, fromZeroSize * sizeof( S24 ) ); - break; - case RTAUDIO_SINT32: - memcpy( buffer, &( ( int* ) buffer_ )[outIndex_], fromOutSize * sizeof( int ) ); - memcpy( &( ( int* ) buffer )[fromOutSize], buffer_, fromZeroSize * sizeof( int ) ); - break; - case RTAUDIO_FLOAT32: - memcpy( buffer, &( ( float* ) buffer_ )[outIndex_], fromOutSize * sizeof( float ) ); - memcpy( &( ( float* ) buffer )[fromOutSize], buffer_, fromZeroSize * sizeof( float ) ); - break; - case RTAUDIO_FLOAT64: - memcpy( buffer, &( ( double* ) buffer_ )[outIndex_], fromOutSize * sizeof( double ) ); - memcpy( &( ( double* ) buffer )[fromOutSize], buffer_, fromZeroSize * sizeof( double ) ); - break; - } - - // update "out" index - outIndex_ += bufferSize; - outIndex_ %= bufferSize_; - - return true; - } - -private: - char* buffer_; - unsigned int bufferSize_; - unsigned int inIndex_; - unsigned int outIndex_; -}; - -//----------------------------------------------------------------------------- - -// In order to satisfy WASAPI's buffer requirements, we need a means of converting sample rate -// between HW and the user. The WasapiResampler class is used to perform this conversion between -// HwIn->UserIn and UserOut->HwOut during the stream callback loop. -class WasapiResampler -{ -public: - WasapiResampler( bool isFloat, unsigned int bitsPerSample, unsigned int channelCount, - unsigned int inSampleRate, unsigned int outSampleRate ) - : _bytesPerSample( bitsPerSample / 8 ) - , _channelCount( channelCount ) - , _sampleRatio( ( float ) outSampleRate / inSampleRate ) - , _transformUnk( NULL ) - , _transform( NULL ) - , _mediaType( NULL ) - , _inputMediaType( NULL ) - , _outputMediaType( NULL ) - - #ifdef __IWMResamplerProps_FWD_DEFINED__ - , _resamplerProps( NULL ) - #endif - { - // 1. Initialization - - MFStartup( MF_VERSION, MFSTARTUP_NOSOCKET ); - - // 2. Create Resampler Transform Object - - CoCreateInstance( CLSID_CResamplerMediaObject, NULL, CLSCTX_INPROC_SERVER, - IID_IUnknown, ( void** ) &_transformUnk ); - - _transformUnk->QueryInterface( IID_PPV_ARGS( &_transform ) ); - - #ifdef __IWMResamplerProps_FWD_DEFINED__ - _transformUnk->QueryInterface( IID_PPV_ARGS( &_resamplerProps ) ); - _resamplerProps->SetHalfFilterLength( 60 ); // best conversion quality - #endif - - // 3. Specify input / output format - - MFCreateMediaType( &_mediaType ); - _mediaType->SetGUID( MF_MT_MAJOR_TYPE, MFMediaType_Audio ); - _mediaType->SetGUID( MF_MT_SUBTYPE, isFloat ? MFAudioFormat_Float : MFAudioFormat_PCM ); - _mediaType->SetUINT32( MF_MT_AUDIO_NUM_CHANNELS, channelCount ); - _mediaType->SetUINT32( MF_MT_AUDIO_SAMPLES_PER_SECOND, inSampleRate ); - _mediaType->SetUINT32( MF_MT_AUDIO_BLOCK_ALIGNMENT, _bytesPerSample * channelCount ); - _mediaType->SetUINT32( MF_MT_AUDIO_AVG_BYTES_PER_SECOND, _bytesPerSample * channelCount * inSampleRate ); - _mediaType->SetUINT32( MF_MT_AUDIO_BITS_PER_SAMPLE, bitsPerSample ); - _mediaType->SetUINT32( MF_MT_ALL_SAMPLES_INDEPENDENT, TRUE ); - - MFCreateMediaType( &_inputMediaType ); - _mediaType->CopyAllItems( _inputMediaType ); - - _transform->SetInputType( 0, _inputMediaType, 0 ); - - MFCreateMediaType( &_outputMediaType ); - _mediaType->CopyAllItems( _outputMediaType ); - - _outputMediaType->SetUINT32( MF_MT_AUDIO_SAMPLES_PER_SECOND, outSampleRate ); - _outputMediaType->SetUINT32( MF_MT_AUDIO_AVG_BYTES_PER_SECOND, _bytesPerSample * channelCount * outSampleRate ); - - _transform->SetOutputType( 0, _outputMediaType, 0 ); - - // 4. Send stream start messages to Resampler - - _transform->ProcessMessage( MFT_MESSAGE_COMMAND_FLUSH, 0 ); - _transform->ProcessMessage( MFT_MESSAGE_NOTIFY_BEGIN_STREAMING, 0 ); - _transform->ProcessMessage( MFT_MESSAGE_NOTIFY_START_OF_STREAM, 0 ); - } - - ~WasapiResampler() - { - // 8. Send stream stop messages to Resampler - - _transform->ProcessMessage( MFT_MESSAGE_NOTIFY_END_OF_STREAM, 0 ); - _transform->ProcessMessage( MFT_MESSAGE_NOTIFY_END_STREAMING, 0 ); - - // 9. Cleanup - - MFShutdown(); - - SAFE_RELEASE( _transformUnk ); - SAFE_RELEASE( _transform ); - SAFE_RELEASE( _mediaType ); - SAFE_RELEASE( _inputMediaType ); - SAFE_RELEASE( _outputMediaType ); - - #ifdef __IWMResamplerProps_FWD_DEFINED__ - SAFE_RELEASE( _resamplerProps ); - #endif - } - - void Convert( char* outBuffer, const char* inBuffer, unsigned int inSampleCount, unsigned int& outSampleCount ) - { - unsigned int inputBufferSize = _bytesPerSample * _channelCount * inSampleCount; - if ( _sampleRatio == 1 ) - { - // no sample rate conversion required - memcpy( outBuffer, inBuffer, inputBufferSize ); - outSampleCount = inSampleCount; - return; - } - - unsigned int outputBufferSize = ( unsigned int ) ceilf( inputBufferSize * _sampleRatio ) + ( _bytesPerSample * _channelCount ); - - IMFMediaBuffer* rInBuffer; - IMFSample* rInSample; - BYTE* rInByteBuffer = NULL; - - // 5. Create Sample object from input data - - MFCreateMemoryBuffer( inputBufferSize, &rInBuffer ); - - rInBuffer->Lock( &rInByteBuffer, NULL, NULL ); - memcpy( rInByteBuffer, inBuffer, inputBufferSize ); - rInBuffer->Unlock(); - rInByteBuffer = NULL; - - rInBuffer->SetCurrentLength( inputBufferSize ); - - MFCreateSample( &rInSample ); - rInSample->AddBuffer( rInBuffer ); - - // 6. Pass input data to Resampler - - _transform->ProcessInput( 0, rInSample, 0 ); - - SAFE_RELEASE( rInBuffer ); - SAFE_RELEASE( rInSample ); - - // 7. Perform sample rate conversion - - IMFMediaBuffer* rOutBuffer = NULL; - BYTE* rOutByteBuffer = NULL; - - MFT_OUTPUT_DATA_BUFFER rOutDataBuffer; - DWORD rStatus; - DWORD rBytes = outputBufferSize; // maximum bytes accepted per ProcessOutput - - // 7.1 Create Sample object for output data - - memset( &rOutDataBuffer, 0, sizeof rOutDataBuffer ); - MFCreateSample( &( rOutDataBuffer.pSample ) ); - MFCreateMemoryBuffer( rBytes, &rOutBuffer ); - rOutDataBuffer.pSample->AddBuffer( rOutBuffer ); - rOutDataBuffer.dwStreamID = 0; - rOutDataBuffer.dwStatus = 0; - rOutDataBuffer.pEvents = NULL; - - // 7.2 Get output data from Resampler - - if ( _transform->ProcessOutput( 0, 1, &rOutDataBuffer, &rStatus ) == MF_E_TRANSFORM_NEED_MORE_INPUT ) - { - outSampleCount = 0; - SAFE_RELEASE( rOutBuffer ); - SAFE_RELEASE( rOutDataBuffer.pSample ); - return; - } - - // 7.3 Write output data to outBuffer - - SAFE_RELEASE( rOutBuffer ); - rOutDataBuffer.pSample->ConvertToContiguousBuffer( &rOutBuffer ); - rOutBuffer->GetCurrentLength( &rBytes ); - - rOutBuffer->Lock( &rOutByteBuffer, NULL, NULL ); - memcpy( outBuffer, rOutByteBuffer, rBytes ); - rOutBuffer->Unlock(); - rOutByteBuffer = NULL; - - outSampleCount = rBytes / _bytesPerSample / _channelCount; - SAFE_RELEASE( rOutBuffer ); - SAFE_RELEASE( rOutDataBuffer.pSample ); - } - -private: - unsigned int _bytesPerSample; - unsigned int _channelCount; - float _sampleRatio; - - IUnknown* _transformUnk; - IMFTransform* _transform; - IMFMediaType* _mediaType; - IMFMediaType* _inputMediaType; - IMFMediaType* _outputMediaType; - - #ifdef __IWMResamplerProps_FWD_DEFINED__ - IWMResamplerProps* _resamplerProps; - #endif -}; - -//----------------------------------------------------------------------------- - -// A structure to hold various information related to the WASAPI implementation. -struct WasapiHandle -{ - IAudioClient* captureAudioClient; - IAudioClient* renderAudioClient; - IAudioCaptureClient* captureClient; - IAudioRenderClient* renderClient; - HANDLE captureEvent; - HANDLE renderEvent; - - WasapiHandle() - : captureAudioClient( NULL ), - renderAudioClient( NULL ), - captureClient( NULL ), - renderClient( NULL ), - captureEvent( NULL ), - renderEvent( NULL ) {} -}; - -//============================================================================= - -RtApiWasapi::RtApiWasapi() - : coInitialized_( false ), deviceEnumerator_( NULL ) -{ - // WASAPI can run either apartment or multi-threaded - HRESULT hr = CoInitialize( NULL ); - if ( !FAILED( hr ) ) - coInitialized_ = true; - - // Instantiate device enumerator - hr = CoCreateInstance( __uuidof( MMDeviceEnumerator ), NULL, - CLSCTX_ALL, __uuidof( IMMDeviceEnumerator ), - ( void** ) &deviceEnumerator_ ); - - // If this runs on an old Windows, it will fail. Ignore and proceed. - if ( FAILED( hr ) ) - deviceEnumerator_ = NULL; -} - -//----------------------------------------------------------------------------- - -RtApiWasapi::~RtApiWasapi() -{ - if ( stream_.state != STREAM_CLOSED ) - closeStream(); - - SAFE_RELEASE( deviceEnumerator_ ); - - // If this object previously called CoInitialize() - if ( coInitialized_ ) - CoUninitialize(); -} - -//============================================================================= - -unsigned int RtApiWasapi::getDeviceCount( void ) -{ - unsigned int captureDeviceCount = 0; - unsigned int renderDeviceCount = 0; - - IMMDeviceCollection* captureDevices = NULL; - IMMDeviceCollection* renderDevices = NULL; - - if ( !deviceEnumerator_ ) - return 0; - - // Count capture devices - errorText_.clear(); - HRESULT hr = deviceEnumerator_->EnumAudioEndpoints( eCapture, DEVICE_STATE_ACTIVE, &captureDevices ); - if ( FAILED( hr ) ) { - errorText_ = "RtApiWasapi::getDeviceCount: Unable to retrieve capture device collection."; - goto Exit; - } - - hr = captureDevices->GetCount( &captureDeviceCount ); - if ( FAILED( hr ) ) { - errorText_ = "RtApiWasapi::getDeviceCount: Unable to retrieve capture device count."; - goto Exit; - } - - // Count render devices - hr = deviceEnumerator_->EnumAudioEndpoints( eRender, DEVICE_STATE_ACTIVE, &renderDevices ); - if ( FAILED( hr ) ) { - errorText_ = "RtApiWasapi::getDeviceCount: Unable to retrieve render device collection."; - goto Exit; - } - - hr = renderDevices->GetCount( &renderDeviceCount ); - if ( FAILED( hr ) ) { - errorText_ = "RtApiWasapi::getDeviceCount: Unable to retrieve render device count."; - goto Exit; - } - -Exit: - // release all references - SAFE_RELEASE( captureDevices ); - SAFE_RELEASE( renderDevices ); - - if ( errorText_.empty() ) - return captureDeviceCount + renderDeviceCount; - - error( RtAudioError::DRIVER_ERROR ); - return 0; -} - -//----------------------------------------------------------------------------- - -RtAudio::DeviceInfo RtApiWasapi::getDeviceInfo( unsigned int device ) -{ - RtAudio::DeviceInfo info; - unsigned int captureDeviceCount = 0; - unsigned int renderDeviceCount = 0; - std::string defaultDeviceName; - bool isCaptureDevice = false; - - PROPVARIANT deviceNameProp; - PROPVARIANT defaultDeviceNameProp; - - IMMDeviceCollection* captureDevices = NULL; - IMMDeviceCollection* renderDevices = NULL; - IMMDevice* devicePtr = NULL; - IMMDevice* defaultDevicePtr = NULL; - IAudioClient* audioClient = NULL; - IPropertyStore* devicePropStore = NULL; - IPropertyStore* defaultDevicePropStore = NULL; - - WAVEFORMATEX* deviceFormat = NULL; - WAVEFORMATEX* closestMatchFormat = NULL; - - // probed - info.probed = false; - - // Count capture devices - errorText_.clear(); - RtAudioError::Type errorType = RtAudioError::DRIVER_ERROR; - HRESULT hr = deviceEnumerator_->EnumAudioEndpoints( eCapture, DEVICE_STATE_ACTIVE, &captureDevices ); - if ( FAILED( hr ) ) { - errorText_ = "RtApiWasapi::getDeviceInfo: Unable to retrieve capture device collection."; - goto Exit; - } - - hr = captureDevices->GetCount( &captureDeviceCount ); - if ( FAILED( hr ) ) { - errorText_ = "RtApiWasapi::getDeviceInfo: Unable to retrieve capture device count."; - goto Exit; - } - - // Count render devices - hr = deviceEnumerator_->EnumAudioEndpoints( eRender, DEVICE_STATE_ACTIVE, &renderDevices ); - if ( FAILED( hr ) ) { - errorText_ = "RtApiWasapi::getDeviceInfo: Unable to retrieve render device collection."; - goto Exit; - } - - hr = renderDevices->GetCount( &renderDeviceCount ); - if ( FAILED( hr ) ) { - errorText_ = "RtApiWasapi::getDeviceInfo: Unable to retrieve render device count."; - goto Exit; - } - - // validate device index - if ( device >= captureDeviceCount + renderDeviceCount ) { - errorText_ = "RtApiWasapi::getDeviceInfo: Invalid device index."; - errorType = RtAudioError::INVALID_USE; - goto Exit; - } - - // determine whether index falls within capture or render devices - if ( device >= renderDeviceCount ) { - hr = captureDevices->Item( device - renderDeviceCount, &devicePtr ); - if ( FAILED( hr ) ) { - errorText_ = "RtApiWasapi::getDeviceInfo: Unable to retrieve capture device handle."; - goto Exit; - } - isCaptureDevice = true; - } - else { - hr = renderDevices->Item( device, &devicePtr ); - if ( FAILED( hr ) ) { - errorText_ = "RtApiWasapi::getDeviceInfo: Unable to retrieve render device handle."; - goto Exit; - } - isCaptureDevice = false; - } - - // get default device name - if ( isCaptureDevice ) { - hr = deviceEnumerator_->GetDefaultAudioEndpoint( eCapture, eConsole, &defaultDevicePtr ); - if ( FAILED( hr ) ) { - errorText_ = "RtApiWasapi::getDeviceInfo: Unable to retrieve default capture device handle."; - goto Exit; - } - } - else { - hr = deviceEnumerator_->GetDefaultAudioEndpoint( eRender, eConsole, &defaultDevicePtr ); - if ( FAILED( hr ) ) { - errorText_ = "RtApiWasapi::getDeviceInfo: Unable to retrieve default render device handle."; - goto Exit; - } - } - - hr = defaultDevicePtr->OpenPropertyStore( STGM_READ, &defaultDevicePropStore ); - if ( FAILED( hr ) ) { - errorText_ = "RtApiWasapi::getDeviceInfo: Unable to open default device property store."; - goto Exit; - } - PropVariantInit( &defaultDeviceNameProp ); - - hr = defaultDevicePropStore->GetValue( PKEY_Device_FriendlyName, &defaultDeviceNameProp ); - if ( FAILED( hr ) ) { - errorText_ = "RtApiWasapi::getDeviceInfo: Unable to retrieve default device property: PKEY_Device_FriendlyName."; - goto Exit; - } - - defaultDeviceName = convertCharPointerToStdString(defaultDeviceNameProp.pwszVal); - - // name - hr = devicePtr->OpenPropertyStore( STGM_READ, &devicePropStore ); - if ( FAILED( hr ) ) { - errorText_ = "RtApiWasapi::getDeviceInfo: Unable to open device property store."; - goto Exit; - } - - PropVariantInit( &deviceNameProp ); - - hr = devicePropStore->GetValue( PKEY_Device_FriendlyName, &deviceNameProp ); - if ( FAILED( hr ) ) { - errorText_ = "RtApiWasapi::getDeviceInfo: Unable to retrieve device property: PKEY_Device_FriendlyName."; - goto Exit; - } - - info.name =convertCharPointerToStdString(deviceNameProp.pwszVal); - - // is default - if ( isCaptureDevice ) { - info.isDefaultInput = info.name == defaultDeviceName; - info.isDefaultOutput = false; - } - else { - info.isDefaultInput = false; - info.isDefaultOutput = info.name == defaultDeviceName; - } - - // channel count - hr = devicePtr->Activate( __uuidof( IAudioClient ), CLSCTX_ALL, NULL, ( void** ) &audioClient ); - if ( FAILED( hr ) ) { - errorText_ = "RtApiWasapi::getDeviceInfo: Unable to retrieve device audio client."; - goto Exit; - } - - hr = audioClient->GetMixFormat( &deviceFormat ); - if ( FAILED( hr ) ) { - errorText_ = "RtApiWasapi::getDeviceInfo: Unable to retrieve device mix format."; - goto Exit; - } - - if ( isCaptureDevice ) { - info.inputChannels = deviceFormat->nChannels; - info.outputChannels = 0; - info.duplexChannels = 0; - } - else { - info.inputChannels = 0; - info.outputChannels = deviceFormat->nChannels; - info.duplexChannels = 0; - } - - // sample rates - info.sampleRates.clear(); - - // allow support for all sample rates as we have a built-in sample rate converter - for ( unsigned int i = 0; i < MAX_SAMPLE_RATES; i++ ) { - info.sampleRates.push_back( SAMPLE_RATES[i] ); - } - info.preferredSampleRate = deviceFormat->nSamplesPerSec; - - // native format - info.nativeFormats = 0; - - if ( deviceFormat->wFormatTag == WAVE_FORMAT_IEEE_FLOAT || - ( deviceFormat->wFormatTag == WAVE_FORMAT_EXTENSIBLE && - ( ( WAVEFORMATEXTENSIBLE* ) deviceFormat )->SubFormat == KSDATAFORMAT_SUBTYPE_IEEE_FLOAT ) ) - { - if ( deviceFormat->wBitsPerSample == 32 ) { - info.nativeFormats |= RTAUDIO_FLOAT32; - } - else if ( deviceFormat->wBitsPerSample == 64 ) { - info.nativeFormats |= RTAUDIO_FLOAT64; - } - } - else if ( deviceFormat->wFormatTag == WAVE_FORMAT_PCM || - ( deviceFormat->wFormatTag == WAVE_FORMAT_EXTENSIBLE && - ( ( WAVEFORMATEXTENSIBLE* ) deviceFormat )->SubFormat == KSDATAFORMAT_SUBTYPE_PCM ) ) - { - if ( deviceFormat->wBitsPerSample == 8 ) { - info.nativeFormats |= RTAUDIO_SINT8; - } - else if ( deviceFormat->wBitsPerSample == 16 ) { - info.nativeFormats |= RTAUDIO_SINT16; - } - else if ( deviceFormat->wBitsPerSample == 24 ) { - info.nativeFormats |= RTAUDIO_SINT24; - } - else if ( deviceFormat->wBitsPerSample == 32 ) { - info.nativeFormats |= RTAUDIO_SINT32; - } - } - - // probed - info.probed = true; - -Exit: - // release all references - PropVariantClear( &deviceNameProp ); - PropVariantClear( &defaultDeviceNameProp ); - - SAFE_RELEASE( captureDevices ); - SAFE_RELEASE( renderDevices ); - SAFE_RELEASE( devicePtr ); - SAFE_RELEASE( defaultDevicePtr ); - SAFE_RELEASE( audioClient ); - SAFE_RELEASE( devicePropStore ); - SAFE_RELEASE( defaultDevicePropStore ); - - CoTaskMemFree( deviceFormat ); - CoTaskMemFree( closestMatchFormat ); - - if ( !errorText_.empty() ) - error( errorType ); - return info; -} - -//----------------------------------------------------------------------------- - -unsigned int RtApiWasapi::getDefaultOutputDevice( void ) -{ - for ( unsigned int i = 0; i < getDeviceCount(); i++ ) { - if ( getDeviceInfo( i ).isDefaultOutput ) { - return i; - } - } - - return 0; -} - -//----------------------------------------------------------------------------- - -unsigned int RtApiWasapi::getDefaultInputDevice( void ) -{ - for ( unsigned int i = 0; i < getDeviceCount(); i++ ) { - if ( getDeviceInfo( i ).isDefaultInput ) { - return i; - } - } - - return 0; -} - -//----------------------------------------------------------------------------- - -void RtApiWasapi::closeStream( void ) -{ - if ( stream_.state == STREAM_CLOSED ) { - errorText_ = "RtApiWasapi::closeStream: No open stream to close."; - error( RtAudioError::WARNING ); - return; - } - - if ( stream_.state != STREAM_STOPPED ) - stopStream(); - - // clean up stream memory - SAFE_RELEASE( ( ( WasapiHandle* ) stream_.apiHandle )->captureAudioClient ) - SAFE_RELEASE( ( ( WasapiHandle* ) stream_.apiHandle )->renderAudioClient ) - - SAFE_RELEASE( ( ( WasapiHandle* ) stream_.apiHandle )->captureClient ) - SAFE_RELEASE( ( ( WasapiHandle* ) stream_.apiHandle )->renderClient ) - - if ( ( ( WasapiHandle* ) stream_.apiHandle )->captureEvent ) - CloseHandle( ( ( WasapiHandle* ) stream_.apiHandle )->captureEvent ); - - if ( ( ( WasapiHandle* ) stream_.apiHandle )->renderEvent ) - CloseHandle( ( ( WasapiHandle* ) stream_.apiHandle )->renderEvent ); - - delete ( WasapiHandle* ) stream_.apiHandle; - stream_.apiHandle = NULL; - - for ( int i = 0; i < 2; i++ ) { - if ( stream_.userBuffer[i] ) { - free( stream_.userBuffer[i] ); - stream_.userBuffer[i] = 0; - } - } - - if ( stream_.deviceBuffer ) { - free( stream_.deviceBuffer ); - stream_.deviceBuffer = 0; - } - - // update stream state - stream_.state = STREAM_CLOSED; -} - -//----------------------------------------------------------------------------- - -void RtApiWasapi::startStream( void ) -{ - verifyStream(); - - if ( stream_.state == STREAM_RUNNING ) { - errorText_ = "RtApiWasapi::startStream: The stream is already running."; - error( RtAudioError::WARNING ); - return; - } - - #if defined( HAVE_GETTIMEOFDAY ) - gettimeofday( &stream_.lastTickTimestamp, NULL ); - #endif - - // update stream state - stream_.state = STREAM_RUNNING; - - // create WASAPI stream thread - stream_.callbackInfo.thread = ( ThreadHandle ) CreateThread( NULL, 0, runWasapiThread, this, CREATE_SUSPENDED, NULL ); - - if ( !stream_.callbackInfo.thread ) { - errorText_ = "RtApiWasapi::startStream: Unable to instantiate callback thread."; - error( RtAudioError::THREAD_ERROR ); - } - else { - SetThreadPriority( ( void* ) stream_.callbackInfo.thread, stream_.callbackInfo.priority ); - ResumeThread( ( void* ) stream_.callbackInfo.thread ); - } -} - -//----------------------------------------------------------------------------- - -void RtApiWasapi::stopStream( void ) -{ - verifyStream(); - - if ( stream_.state == STREAM_STOPPED ) { - errorText_ = "RtApiWasapi::stopStream: The stream is already stopped."; - error( RtAudioError::WARNING ); - return; - } - - // inform stream thread by setting stream state to STREAM_STOPPING - stream_.state = STREAM_STOPPING; - - // wait until stream thread is stopped - while( stream_.state != STREAM_STOPPED ) { - Sleep( 1 ); - } - - // Wait for the last buffer to play before stopping. - Sleep( 1000 * stream_.bufferSize / stream_.sampleRate ); - - // close thread handle - if ( stream_.callbackInfo.thread && !CloseHandle( ( void* ) stream_.callbackInfo.thread ) ) { - errorText_ = "RtApiWasapi::stopStream: Unable to close callback thread."; - error( RtAudioError::THREAD_ERROR ); - return; - } - - stream_.callbackInfo.thread = (ThreadHandle) NULL; -} - -//----------------------------------------------------------------------------- - -void RtApiWasapi::abortStream( void ) -{ - verifyStream(); - - if ( stream_.state == STREAM_STOPPED ) { - errorText_ = "RtApiWasapi::abortStream: The stream is already stopped."; - error( RtAudioError::WARNING ); - return; - } - - // inform stream thread by setting stream state to STREAM_STOPPING - stream_.state = STREAM_STOPPING; - - // wait until stream thread is stopped - while ( stream_.state != STREAM_STOPPED ) { - Sleep( 1 ); - } - - // close thread handle - if ( stream_.callbackInfo.thread && !CloseHandle( ( void* ) stream_.callbackInfo.thread ) ) { - errorText_ = "RtApiWasapi::abortStream: Unable to close callback thread."; - error( RtAudioError::THREAD_ERROR ); - return; - } - - stream_.callbackInfo.thread = (ThreadHandle) NULL; -} - -//----------------------------------------------------------------------------- - -bool RtApiWasapi::probeDeviceOpen( unsigned int device, StreamMode mode, unsigned int channels, - unsigned int firstChannel, unsigned int sampleRate, - RtAudioFormat format, unsigned int* bufferSize, - RtAudio::StreamOptions* options ) -{ - bool methodResult = FAILURE; - unsigned int captureDeviceCount = 0; - unsigned int renderDeviceCount = 0; - - IMMDeviceCollection* captureDevices = NULL; - IMMDeviceCollection* renderDevices = NULL; - IMMDevice* devicePtr = NULL; - WAVEFORMATEX* deviceFormat = NULL; - unsigned int bufferBytes; - stream_.state = STREAM_STOPPED; - - // create API Handle if not already created - if ( !stream_.apiHandle ) - stream_.apiHandle = ( void* ) new WasapiHandle(); - - // Count capture devices - errorText_.clear(); - RtAudioError::Type errorType = RtAudioError::DRIVER_ERROR; - HRESULT hr = deviceEnumerator_->EnumAudioEndpoints( eCapture, DEVICE_STATE_ACTIVE, &captureDevices ); - if ( FAILED( hr ) ) { - errorText_ = "RtApiWasapi::probeDeviceOpen: Unable to retrieve capture device collection."; - goto Exit; - } - - hr = captureDevices->GetCount( &captureDeviceCount ); - if ( FAILED( hr ) ) { - errorText_ = "RtApiWasapi::probeDeviceOpen: Unable to retrieve capture device count."; - goto Exit; - } - - // Count render devices - hr = deviceEnumerator_->EnumAudioEndpoints( eRender, DEVICE_STATE_ACTIVE, &renderDevices ); - if ( FAILED( hr ) ) { - errorText_ = "RtApiWasapi::probeDeviceOpen: Unable to retrieve render device collection."; - goto Exit; - } - - hr = renderDevices->GetCount( &renderDeviceCount ); - if ( FAILED( hr ) ) { - errorText_ = "RtApiWasapi::probeDeviceOpen: Unable to retrieve render device count."; - goto Exit; - } - - // validate device index - if ( device >= captureDeviceCount + renderDeviceCount ) { - errorType = RtAudioError::INVALID_USE; - errorText_ = "RtApiWasapi::probeDeviceOpen: Invalid device index."; - goto Exit; - } - - // if device index falls within capture devices - if ( device >= renderDeviceCount ) { - if ( mode != INPUT ) { - errorType = RtAudioError::INVALID_USE; - errorText_ = "RtApiWasapi::probeDeviceOpen: Capture device selected as output device."; - goto Exit; - } - - // retrieve captureAudioClient from devicePtr - IAudioClient*& captureAudioClient = ( ( WasapiHandle* ) stream_.apiHandle )->captureAudioClient; - - hr = captureDevices->Item( device - renderDeviceCount, &devicePtr ); - if ( FAILED( hr ) ) { - errorText_ = "RtApiWasapi::probeDeviceOpen: Unable to retrieve capture device handle."; - goto Exit; - } - - hr = devicePtr->Activate( __uuidof( IAudioClient ), CLSCTX_ALL, - NULL, ( void** ) &captureAudioClient ); - if ( FAILED( hr ) ) { - errorText_ = "RtApiWasapi::probeDeviceOpen: Unable to retrieve capture device audio client."; - goto Exit; - } - - hr = captureAudioClient->GetMixFormat( &deviceFormat ); - if ( FAILED( hr ) ) { - errorText_ = "RtApiWasapi::probeDeviceOpen: Unable to retrieve capture device mix format."; - goto Exit; - } - - stream_.nDeviceChannels[mode] = deviceFormat->nChannels; - captureAudioClient->GetStreamLatency( ( long long* ) &stream_.latency[mode] ); - } - - // if device index falls within render devices and is configured for loopback - if ( device < renderDeviceCount && mode == INPUT ) - { - // if renderAudioClient is not initialised, initialise it now - IAudioClient*& renderAudioClient = ( ( WasapiHandle* ) stream_.apiHandle )->renderAudioClient; - if ( !renderAudioClient ) - { - probeDeviceOpen( device, OUTPUT, channels, firstChannel, sampleRate, format, bufferSize, options ); - } - - // retrieve captureAudioClient from devicePtr - IAudioClient*& captureAudioClient = ( ( WasapiHandle* ) stream_.apiHandle )->captureAudioClient; - - hr = renderDevices->Item( device, &devicePtr ); - if ( FAILED( hr ) ) { - errorText_ = "RtApiWasapi::probeDeviceOpen: Unable to retrieve render device handle."; - goto Exit; - } - - hr = devicePtr->Activate( __uuidof( IAudioClient ), CLSCTX_ALL, - NULL, ( void** ) &captureAudioClient ); - if ( FAILED( hr ) ) { - errorText_ = "RtApiWasapi::probeDeviceOpen: Unable to retrieve render device audio client."; - goto Exit; - } - - hr = captureAudioClient->GetMixFormat( &deviceFormat ); - if ( FAILED( hr ) ) { - errorText_ = "RtApiWasapi::probeDeviceOpen: Unable to retrieve render device mix format."; - goto Exit; - } - - stream_.nDeviceChannels[mode] = deviceFormat->nChannels; - captureAudioClient->GetStreamLatency( ( long long* ) &stream_.latency[mode] ); - } - - // if device index falls within render devices and is configured for output - if ( device < renderDeviceCount && mode == OUTPUT ) - { - // if renderAudioClient is already initialised, don't initialise it again - IAudioClient*& renderAudioClient = ( ( WasapiHandle* ) stream_.apiHandle )->renderAudioClient; - if ( renderAudioClient ) - { - methodResult = SUCCESS; - goto Exit; - } - - hr = renderDevices->Item( device, &devicePtr ); - if ( FAILED( hr ) ) { - errorText_ = "RtApiWasapi::probeDeviceOpen: Unable to retrieve render device handle."; - goto Exit; - } - - hr = devicePtr->Activate( __uuidof( IAudioClient ), CLSCTX_ALL, - NULL, ( void** ) &renderAudioClient ); - if ( FAILED( hr ) ) { - errorText_ = "RtApiWasapi::probeDeviceOpen: Unable to retrieve render device audio client."; - goto Exit; - } - - hr = renderAudioClient->GetMixFormat( &deviceFormat ); - if ( FAILED( hr ) ) { - errorText_ = "RtApiWasapi::probeDeviceOpen: Unable to retrieve render device mix format."; - goto Exit; - } - - stream_.nDeviceChannels[mode] = deviceFormat->nChannels; - renderAudioClient->GetStreamLatency( ( long long* ) &stream_.latency[mode] ); - } - - // fill stream data - if ( ( stream_.mode == OUTPUT && mode == INPUT ) || - ( stream_.mode == INPUT && mode == OUTPUT ) ) { - stream_.mode = DUPLEX; - } - else { - stream_.mode = mode; - } - - stream_.device[mode] = device; - stream_.doByteSwap[mode] = false; - stream_.sampleRate = sampleRate; - stream_.bufferSize = *bufferSize; - stream_.nBuffers = 1; - stream_.nUserChannels[mode] = channels; - stream_.channelOffset[mode] = firstChannel; - stream_.userFormat = format; - stream_.deviceFormat[mode] = getDeviceInfo( device ).nativeFormats; - - if ( options && options->flags & RTAUDIO_NONINTERLEAVED ) - stream_.userInterleaved = false; - else - stream_.userInterleaved = true; - stream_.deviceInterleaved[mode] = true; - - // Set flags for buffer conversion. - stream_.doConvertBuffer[mode] = false; - if ( stream_.userFormat != stream_.deviceFormat[mode] || - stream_.nUserChannels[0] != stream_.nDeviceChannels[0] || - stream_.nUserChannels[1] != stream_.nDeviceChannels[1] ) - stream_.doConvertBuffer[mode] = true; - else if ( stream_.userInterleaved != stream_.deviceInterleaved[mode] && - stream_.nUserChannels[mode] > 1 ) - stream_.doConvertBuffer[mode] = true; - - if ( stream_.doConvertBuffer[mode] ) - setConvertInfo( mode, 0 ); - - // Allocate necessary internal buffers - bufferBytes = stream_.nUserChannels[mode] * stream_.bufferSize * formatBytes( stream_.userFormat ); - - stream_.userBuffer[mode] = ( char* ) calloc( bufferBytes, 1 ); - if ( !stream_.userBuffer[mode] ) { - errorType = RtAudioError::MEMORY_ERROR; - errorText_ = "RtApiWasapi::probeDeviceOpen: Error allocating user buffer memory."; - goto Exit; - } - - if ( options && options->flags & RTAUDIO_SCHEDULE_REALTIME ) - stream_.callbackInfo.priority = 15; - else - stream_.callbackInfo.priority = 0; - - ///! TODO: RTAUDIO_MINIMIZE_LATENCY // Provide stream buffers directly to callback - ///! TODO: RTAUDIO_HOG_DEVICE // Exclusive mode - - methodResult = SUCCESS; - -Exit: - //clean up - SAFE_RELEASE( captureDevices ); - SAFE_RELEASE( renderDevices ); - SAFE_RELEASE( devicePtr ); - CoTaskMemFree( deviceFormat ); - - // if method failed, close the stream - if ( methodResult == FAILURE ) - closeStream(); - - if ( !errorText_.empty() ) - error( errorType ); - return methodResult; -} - -//============================================================================= - -DWORD WINAPI RtApiWasapi::runWasapiThread( void* wasapiPtr ) -{ - if ( wasapiPtr ) - ( ( RtApiWasapi* ) wasapiPtr )->wasapiThread(); - - return 0; -} - -DWORD WINAPI RtApiWasapi::stopWasapiThread( void* wasapiPtr ) -{ - if ( wasapiPtr ) - ( ( RtApiWasapi* ) wasapiPtr )->stopStream(); - - return 0; -} - -DWORD WINAPI RtApiWasapi::abortWasapiThread( void* wasapiPtr ) -{ - if ( wasapiPtr ) - ( ( RtApiWasapi* ) wasapiPtr )->abortStream(); - - return 0; -} - -//----------------------------------------------------------------------------- - -void RtApiWasapi::wasapiThread() -{ - // as this is a new thread, we must CoInitialize it - CoInitialize( NULL ); - - HRESULT hr; - - IAudioClient* captureAudioClient = ( ( WasapiHandle* ) stream_.apiHandle )->captureAudioClient; - IAudioClient* renderAudioClient = ( ( WasapiHandle* ) stream_.apiHandle )->renderAudioClient; - IAudioCaptureClient* captureClient = ( ( WasapiHandle* ) stream_.apiHandle )->captureClient; - IAudioRenderClient* renderClient = ( ( WasapiHandle* ) stream_.apiHandle )->renderClient; - HANDLE captureEvent = ( ( WasapiHandle* ) stream_.apiHandle )->captureEvent; - HANDLE renderEvent = ( ( WasapiHandle* ) stream_.apiHandle )->renderEvent; - - WAVEFORMATEX* captureFormat = NULL; - WAVEFORMATEX* renderFormat = NULL; - float captureSrRatio = 0.0f; - float renderSrRatio = 0.0f; - WasapiBuffer captureBuffer; - WasapiBuffer renderBuffer; - WasapiResampler* captureResampler = NULL; - WasapiResampler* renderResampler = NULL; - - // declare local stream variables - RtAudioCallback callback = ( RtAudioCallback ) stream_.callbackInfo.callback; - BYTE* streamBuffer = NULL; - unsigned long captureFlags = 0; - unsigned int bufferFrameCount = 0; - unsigned int numFramesPadding = 0; - unsigned int convBufferSize = 0; - bool loopbackEnabled = stream_.device[INPUT] == stream_.device[OUTPUT]; - bool callbackPushed = true; - bool callbackPulled = false; - bool callbackStopped = false; - int callbackResult = 0; - - // convBuffer is used to store converted buffers between WASAPI and the user - char* convBuffer = NULL; - unsigned int convBuffSize = 0; - unsigned int deviceBuffSize = 0; - - std::string errorText; - RtAudioError::Type errorType = RtAudioError::DRIVER_ERROR; - - // Attempt to assign "Pro Audio" characteristic to thread - HMODULE AvrtDll = LoadLibrary( (LPCTSTR) "AVRT.dll" ); - if ( AvrtDll ) { - DWORD taskIndex = 0; - TAvSetMmThreadCharacteristicsPtr AvSetMmThreadCharacteristicsPtr = - ( TAvSetMmThreadCharacteristicsPtr ) (void(*)()) GetProcAddress( AvrtDll, "AvSetMmThreadCharacteristicsW" ); - AvSetMmThreadCharacteristicsPtr( L"Pro Audio", &taskIndex ); - FreeLibrary( AvrtDll ); - } - - // start capture stream if applicable - if ( captureAudioClient ) { - hr = captureAudioClient->GetMixFormat( &captureFormat ); - if ( FAILED( hr ) ) { - errorText = "RtApiWasapi::wasapiThread: Unable to retrieve device mix format."; - goto Exit; - } - - // init captureResampler - captureResampler = new WasapiResampler( stream_.deviceFormat[INPUT] == RTAUDIO_FLOAT32 || stream_.deviceFormat[INPUT] == RTAUDIO_FLOAT64, - formatBytes( stream_.deviceFormat[INPUT] ) * 8, stream_.nDeviceChannels[INPUT], - captureFormat->nSamplesPerSec, stream_.sampleRate ); - - captureSrRatio = ( ( float ) captureFormat->nSamplesPerSec / stream_.sampleRate ); - - if ( !captureClient ) { - hr = captureAudioClient->Initialize( AUDCLNT_SHAREMODE_SHARED, - loopbackEnabled ? AUDCLNT_STREAMFLAGS_LOOPBACK : AUDCLNT_STREAMFLAGS_EVENTCALLBACK, - 0, - 0, - captureFormat, - NULL ); - if ( FAILED( hr ) ) { - errorText = "RtApiWasapi::wasapiThread: Unable to initialize capture audio client."; - goto Exit; - } - - hr = captureAudioClient->GetService( __uuidof( IAudioCaptureClient ), - ( void** ) &captureClient ); - if ( FAILED( hr ) ) { - errorText = "RtApiWasapi::wasapiThread: Unable to retrieve capture client handle."; - goto Exit; - } - - // don't configure captureEvent if in loopback mode - if ( !loopbackEnabled ) - { - // configure captureEvent to trigger on every available capture buffer - captureEvent = CreateEvent( NULL, FALSE, FALSE, NULL ); - if ( !captureEvent ) { - errorType = RtAudioError::SYSTEM_ERROR; - errorText = "RtApiWasapi::wasapiThread: Unable to create capture event."; - goto Exit; - } - - hr = captureAudioClient->SetEventHandle( captureEvent ); - if ( FAILED( hr ) ) { - errorText = "RtApiWasapi::wasapiThread: Unable to set capture event handle."; - goto Exit; - } - - ( ( WasapiHandle* ) stream_.apiHandle )->captureEvent = captureEvent; - } - - ( ( WasapiHandle* ) stream_.apiHandle )->captureClient = captureClient; - - // reset the capture stream - hr = captureAudioClient->Reset(); - if ( FAILED( hr ) ) { - errorText = "RtApiWasapi::wasapiThread: Unable to reset capture stream."; - goto Exit; - } - - // start the capture stream - hr = captureAudioClient->Start(); - if ( FAILED( hr ) ) { - errorText = "RtApiWasapi::wasapiThread: Unable to start capture stream."; - goto Exit; - } - } - - unsigned int inBufferSize = 0; - hr = captureAudioClient->GetBufferSize( &inBufferSize ); - if ( FAILED( hr ) ) { - errorText = "RtApiWasapi::wasapiThread: Unable to get capture buffer size."; - goto Exit; - } - - // scale outBufferSize according to stream->user sample rate ratio - unsigned int outBufferSize = ( unsigned int ) ceilf( stream_.bufferSize * captureSrRatio ) * stream_.nDeviceChannels[INPUT]; - inBufferSize *= stream_.nDeviceChannels[INPUT]; - - // set captureBuffer size - captureBuffer.setBufferSize( inBufferSize + outBufferSize, formatBytes( stream_.deviceFormat[INPUT] ) ); - } - - // start render stream if applicable - if ( renderAudioClient ) { - hr = renderAudioClient->GetMixFormat( &renderFormat ); - if ( FAILED( hr ) ) { - errorText = "RtApiWasapi::wasapiThread: Unable to retrieve device mix format."; - goto Exit; - } - - // init renderResampler - renderResampler = new WasapiResampler( stream_.deviceFormat[OUTPUT] == RTAUDIO_FLOAT32 || stream_.deviceFormat[OUTPUT] == RTAUDIO_FLOAT64, - formatBytes( stream_.deviceFormat[OUTPUT] ) * 8, stream_.nDeviceChannels[OUTPUT], - stream_.sampleRate, renderFormat->nSamplesPerSec ); - - renderSrRatio = ( ( float ) renderFormat->nSamplesPerSec / stream_.sampleRate ); - - if ( !renderClient ) { - hr = renderAudioClient->Initialize( AUDCLNT_SHAREMODE_SHARED, - AUDCLNT_STREAMFLAGS_EVENTCALLBACK, - 0, - 0, - renderFormat, - NULL ); - if ( FAILED( hr ) ) { - errorText = "RtApiWasapi::wasapiThread: Unable to initialize render audio client."; - goto Exit; - } - - hr = renderAudioClient->GetService( __uuidof( IAudioRenderClient ), - ( void** ) &renderClient ); - if ( FAILED( hr ) ) { - errorText = "RtApiWasapi::wasapiThread: Unable to retrieve render client handle."; - goto Exit; - } - - // configure renderEvent to trigger on every available render buffer - renderEvent = CreateEvent( NULL, FALSE, FALSE, NULL ); - if ( !renderEvent ) { - errorType = RtAudioError::SYSTEM_ERROR; - errorText = "RtApiWasapi::wasapiThread: Unable to create render event."; - goto Exit; - } - - hr = renderAudioClient->SetEventHandle( renderEvent ); - if ( FAILED( hr ) ) { - errorText = "RtApiWasapi::wasapiThread: Unable to set render event handle."; - goto Exit; - } - - ( ( WasapiHandle* ) stream_.apiHandle )->renderClient = renderClient; - ( ( WasapiHandle* ) stream_.apiHandle )->renderEvent = renderEvent; - - // reset the render stream - hr = renderAudioClient->Reset(); - if ( FAILED( hr ) ) { - errorText = "RtApiWasapi::wasapiThread: Unable to reset render stream."; - goto Exit; - } - - // start the render stream - hr = renderAudioClient->Start(); - if ( FAILED( hr ) ) { - errorText = "RtApiWasapi::wasapiThread: Unable to start render stream."; - goto Exit; - } - } - - unsigned int outBufferSize = 0; - hr = renderAudioClient->GetBufferSize( &outBufferSize ); - if ( FAILED( hr ) ) { - errorText = "RtApiWasapi::wasapiThread: Unable to get render buffer size."; - goto Exit; - } - - // scale inBufferSize according to user->stream sample rate ratio - unsigned int inBufferSize = ( unsigned int ) ceilf( stream_.bufferSize * renderSrRatio ) * stream_.nDeviceChannels[OUTPUT]; - outBufferSize *= stream_.nDeviceChannels[OUTPUT]; - - // set renderBuffer size - renderBuffer.setBufferSize( inBufferSize + outBufferSize, formatBytes( stream_.deviceFormat[OUTPUT] ) ); - } - - // malloc buffer memory - if ( stream_.mode == INPUT ) - { - using namespace std; // for ceilf - convBuffSize = ( size_t ) ( ceilf( stream_.bufferSize * captureSrRatio ) ) * stream_.nDeviceChannels[INPUT] * formatBytes( stream_.deviceFormat[INPUT] ); - deviceBuffSize = stream_.bufferSize * stream_.nDeviceChannels[INPUT] * formatBytes( stream_.deviceFormat[INPUT] ); - } - else if ( stream_.mode == OUTPUT ) - { - convBuffSize = ( size_t ) ( ceilf( stream_.bufferSize * renderSrRatio ) ) * stream_.nDeviceChannels[OUTPUT] * formatBytes( stream_.deviceFormat[OUTPUT] ); - deviceBuffSize = stream_.bufferSize * stream_.nDeviceChannels[OUTPUT] * formatBytes( stream_.deviceFormat[OUTPUT] ); - } - else if ( stream_.mode == DUPLEX ) - { - convBuffSize = std::max( ( size_t ) ( ceilf( stream_.bufferSize * captureSrRatio ) ) * stream_.nDeviceChannels[INPUT] * formatBytes( stream_.deviceFormat[INPUT] ), - ( size_t ) ( ceilf( stream_.bufferSize * renderSrRatio ) ) * stream_.nDeviceChannels[OUTPUT] * formatBytes( stream_.deviceFormat[OUTPUT] ) ); - deviceBuffSize = std::max( stream_.bufferSize * stream_.nDeviceChannels[INPUT] * formatBytes( stream_.deviceFormat[INPUT] ), - stream_.bufferSize * stream_.nDeviceChannels[OUTPUT] * formatBytes( stream_.deviceFormat[OUTPUT] ) ); - } - - convBuffSize *= 2; // allow overflow for *SrRatio remainders - convBuffer = ( char* ) calloc( convBuffSize, 1 ); - stream_.deviceBuffer = ( char* ) calloc( deviceBuffSize, 1 ); - if ( !convBuffer || !stream_.deviceBuffer ) { - errorType = RtAudioError::MEMORY_ERROR; - errorText = "RtApiWasapi::wasapiThread: Error allocating device buffer memory."; - goto Exit; - } - - // stream process loop - while ( stream_.state != STREAM_STOPPING ) { - if ( !callbackPulled ) { - // Callback Input - // ============== - // 1. Pull callback buffer from inputBuffer - // 2. If 1. was successful: Convert callback buffer to user sample rate and channel count - // Convert callback buffer to user format - - if ( captureAudioClient ) - { - int samplesToPull = ( unsigned int ) floorf( stream_.bufferSize * captureSrRatio ); - if ( captureSrRatio != 1 ) - { - // account for remainders - samplesToPull--; - } - - convBufferSize = 0; - while ( convBufferSize < stream_.bufferSize ) - { - // Pull callback buffer from inputBuffer - callbackPulled = captureBuffer.pullBuffer( convBuffer, - samplesToPull * stream_.nDeviceChannels[INPUT], - stream_.deviceFormat[INPUT] ); - - if ( !callbackPulled ) - { - break; - } - - // Convert callback buffer to user sample rate - unsigned int deviceBufferOffset = convBufferSize * stream_.nDeviceChannels[INPUT] * formatBytes( stream_.deviceFormat[INPUT] ); - unsigned int convSamples = 0; - - captureResampler->Convert( stream_.deviceBuffer + deviceBufferOffset, - convBuffer, - samplesToPull, - convSamples ); - - convBufferSize += convSamples; - samplesToPull = 1; // now pull one sample at a time until we have stream_.bufferSize samples - } - - if ( callbackPulled ) - { - if ( stream_.doConvertBuffer[INPUT] ) { - // Convert callback buffer to user format - convertBuffer( stream_.userBuffer[INPUT], - stream_.deviceBuffer, - stream_.convertInfo[INPUT] ); - } - else { - // no further conversion, simple copy deviceBuffer to userBuffer - memcpy( stream_.userBuffer[INPUT], - stream_.deviceBuffer, - stream_.bufferSize * stream_.nUserChannels[INPUT] * formatBytes( stream_.userFormat ) ); - } - } - } - else { - // if there is no capture stream, set callbackPulled flag - callbackPulled = true; - } - - // Execute Callback - // ================ - // 1. Execute user callback method - // 2. Handle return value from callback - - // if callback has not requested the stream to stop - if ( callbackPulled && !callbackStopped ) { - // Execute user callback method - callbackResult = callback( stream_.userBuffer[OUTPUT], - stream_.userBuffer[INPUT], - stream_.bufferSize, - getStreamTime(), - captureFlags & AUDCLNT_BUFFERFLAGS_DATA_DISCONTINUITY ? RTAUDIO_INPUT_OVERFLOW : 0, - stream_.callbackInfo.userData ); - - // tick stream time - RtApi::tickStreamTime(); - - // Handle return value from callback - if ( callbackResult == 1 ) { - // instantiate a thread to stop this thread - HANDLE threadHandle = CreateThread( NULL, 0, stopWasapiThread, this, 0, NULL ); - if ( !threadHandle ) { - errorType = RtAudioError::THREAD_ERROR; - errorText = "RtApiWasapi::wasapiThread: Unable to instantiate stream stop thread."; - goto Exit; - } - else if ( !CloseHandle( threadHandle ) ) { - errorType = RtAudioError::THREAD_ERROR; - errorText = "RtApiWasapi::wasapiThread: Unable to close stream stop thread handle."; - goto Exit; - } - - callbackStopped = true; - } - else if ( callbackResult == 2 ) { - // instantiate a thread to stop this thread - HANDLE threadHandle = CreateThread( NULL, 0, abortWasapiThread, this, 0, NULL ); - if ( !threadHandle ) { - errorType = RtAudioError::THREAD_ERROR; - errorText = "RtApiWasapi::wasapiThread: Unable to instantiate stream abort thread."; - goto Exit; - } - else if ( !CloseHandle( threadHandle ) ) { - errorType = RtAudioError::THREAD_ERROR; - errorText = "RtApiWasapi::wasapiThread: Unable to close stream abort thread handle."; - goto Exit; - } - - callbackStopped = true; - } - } - } - - // Callback Output - // =============== - // 1. Convert callback buffer to stream format - // 2. Convert callback buffer to stream sample rate and channel count - // 3. Push callback buffer into outputBuffer - - if ( renderAudioClient && callbackPulled ) - { - // if the last call to renderBuffer.PushBuffer() was successful - if ( callbackPushed || convBufferSize == 0 ) - { - if ( stream_.doConvertBuffer[OUTPUT] ) - { - // Convert callback buffer to stream format - convertBuffer( stream_.deviceBuffer, - stream_.userBuffer[OUTPUT], - stream_.convertInfo[OUTPUT] ); - - } - else { - // no further conversion, simple copy userBuffer to deviceBuffer - memcpy( stream_.deviceBuffer, - stream_.userBuffer[OUTPUT], - stream_.bufferSize * stream_.nUserChannels[OUTPUT] * formatBytes( stream_.userFormat ) ); - } - - // Convert callback buffer to stream sample rate - renderResampler->Convert( convBuffer, - stream_.deviceBuffer, - stream_.bufferSize, - convBufferSize ); - } - - // Push callback buffer into outputBuffer - callbackPushed = renderBuffer.pushBuffer( convBuffer, - convBufferSize * stream_.nDeviceChannels[OUTPUT], - stream_.deviceFormat[OUTPUT] ); - } - else { - // if there is no render stream, set callbackPushed flag - callbackPushed = true; - } - - // Stream Capture - // ============== - // 1. Get capture buffer from stream - // 2. Push capture buffer into inputBuffer - // 3. If 2. was successful: Release capture buffer - - if ( captureAudioClient ) { - // if the callback input buffer was not pulled from captureBuffer, wait for next capture event - if ( !callbackPulled ) { - WaitForSingleObject( loopbackEnabled ? renderEvent : captureEvent, INFINITE ); - } - - // Get capture buffer from stream - hr = captureClient->GetBuffer( &streamBuffer, - &bufferFrameCount, - &captureFlags, NULL, NULL ); - if ( FAILED( hr ) ) { - errorText = "RtApiWasapi::wasapiThread: Unable to retrieve capture buffer."; - goto Exit; - } - - if ( bufferFrameCount != 0 ) { - // Push capture buffer into inputBuffer - if ( captureBuffer.pushBuffer( ( char* ) streamBuffer, - bufferFrameCount * stream_.nDeviceChannels[INPUT], - stream_.deviceFormat[INPUT] ) ) - { - // Release capture buffer - hr = captureClient->ReleaseBuffer( bufferFrameCount ); - if ( FAILED( hr ) ) { - errorText = "RtApiWasapi::wasapiThread: Unable to release capture buffer."; - goto Exit; - } - } - else - { - // Inform WASAPI that capture was unsuccessful - hr = captureClient->ReleaseBuffer( 0 ); - if ( FAILED( hr ) ) { - errorText = "RtApiWasapi::wasapiThread: Unable to release capture buffer."; - goto Exit; - } - } - } - else - { - // Inform WASAPI that capture was unsuccessful - hr = captureClient->ReleaseBuffer( 0 ); - if ( FAILED( hr ) ) { - errorText = "RtApiWasapi::wasapiThread: Unable to release capture buffer."; - goto Exit; - } - } - } - - // Stream Render - // ============= - // 1. Get render buffer from stream - // 2. Pull next buffer from outputBuffer - // 3. If 2. was successful: Fill render buffer with next buffer - // Release render buffer - - if ( renderAudioClient ) { - // if the callback output buffer was not pushed to renderBuffer, wait for next render event - if ( callbackPulled && !callbackPushed ) { - WaitForSingleObject( renderEvent, INFINITE ); - } - - // Get render buffer from stream - hr = renderAudioClient->GetBufferSize( &bufferFrameCount ); - if ( FAILED( hr ) ) { - errorText = "RtApiWasapi::wasapiThread: Unable to retrieve render buffer size."; - goto Exit; - } - - hr = renderAudioClient->GetCurrentPadding( &numFramesPadding ); - if ( FAILED( hr ) ) { - errorText = "RtApiWasapi::wasapiThread: Unable to retrieve render buffer padding."; - goto Exit; - } - - bufferFrameCount -= numFramesPadding; - - if ( bufferFrameCount != 0 ) { - hr = renderClient->GetBuffer( bufferFrameCount, &streamBuffer ); - if ( FAILED( hr ) ) { - errorText = "RtApiWasapi::wasapiThread: Unable to retrieve render buffer."; - goto Exit; - } - - // Pull next buffer from outputBuffer - // Fill render buffer with next buffer - if ( renderBuffer.pullBuffer( ( char* ) streamBuffer, - bufferFrameCount * stream_.nDeviceChannels[OUTPUT], - stream_.deviceFormat[OUTPUT] ) ) - { - // Release render buffer - hr = renderClient->ReleaseBuffer( bufferFrameCount, 0 ); - if ( FAILED( hr ) ) { - errorText = "RtApiWasapi::wasapiThread: Unable to release render buffer."; - goto Exit; - } - } - else - { - // Inform WASAPI that render was unsuccessful - hr = renderClient->ReleaseBuffer( 0, 0 ); - if ( FAILED( hr ) ) { - errorText = "RtApiWasapi::wasapiThread: Unable to release render buffer."; - goto Exit; - } - } - } - else - { - // Inform WASAPI that render was unsuccessful - hr = renderClient->ReleaseBuffer( 0, 0 ); - if ( FAILED( hr ) ) { - errorText = "RtApiWasapi::wasapiThread: Unable to release render buffer."; - goto Exit; - } - } - } - - // if the callback buffer was pushed renderBuffer reset callbackPulled flag - if ( callbackPushed ) { - // unsetting the callbackPulled flag lets the stream know that - // the audio device is ready for another callback output buffer. - callbackPulled = false; - } - - } - -Exit: - // clean up - CoTaskMemFree( captureFormat ); - CoTaskMemFree( renderFormat ); - - free ( convBuffer ); - delete renderResampler; - delete captureResampler; - - CoUninitialize(); - - // update stream state - stream_.state = STREAM_STOPPED; - - if ( !errorText.empty() ) - { - errorText_ = errorText; - error( errorType ); - } -} - -//******************** End of __WINDOWS_WASAPI__ *********************// -#endif - - -#if defined(__WINDOWS_DS__) // Windows DirectSound API - -// Modified by Robin Davies, October 2005 -// - Improvements to DirectX pointer chasing. -// - Bug fix for non-power-of-two Asio granularity used by Edirol PCR-A30. -// - Auto-call CoInitialize for DSOUND and ASIO platforms. -// Various revisions for RtAudio 4.0 by Gary Scavone, April 2007 -// Changed device query structure for RtAudio 4.0.7, January 2010 - -#include -#include -#include -#include -#include -#include -#include - -#if defined(__MINGW32__) - // missing from latest mingw winapi -#define WAVE_FORMAT_96M08 0x00010000 /* 96 kHz, Mono, 8-bit */ -#define WAVE_FORMAT_96S08 0x00020000 /* 96 kHz, Stereo, 8-bit */ -#define WAVE_FORMAT_96M16 0x00040000 /* 96 kHz, Mono, 16-bit */ -#define WAVE_FORMAT_96S16 0x00080000 /* 96 kHz, Stereo, 16-bit */ -#endif - -#define MINIMUM_DEVICE_BUFFER_SIZE 32768 - -#ifdef _MSC_VER // if Microsoft Visual C++ -#pragma comment( lib, "winmm.lib" ) // then, auto-link winmm.lib. Otherwise, it has to be added manually. -#endif - -static inline DWORD dsPointerBetween( DWORD pointer, DWORD laterPointer, DWORD earlierPointer, DWORD bufferSize ) -{ - if ( pointer > bufferSize ) pointer -= bufferSize; - if ( laterPointer < earlierPointer ) laterPointer += bufferSize; - if ( pointer < earlierPointer ) pointer += bufferSize; - return pointer >= earlierPointer && pointer < laterPointer; -} - -// A structure to hold various information related to the DirectSound -// API implementation. -struct DsHandle { - unsigned int drainCounter; // Tracks callback counts when draining - bool internalDrain; // Indicates if stop is initiated from callback or not. - void *id[2]; - void *buffer[2]; - bool xrun[2]; - UINT bufferPointer[2]; - DWORD dsBufferSize[2]; - DWORD dsPointerLeadTime[2]; // the number of bytes ahead of the safe pointer to lead by. - HANDLE condition; - - DsHandle() - :drainCounter(0), internalDrain(false) { id[0] = 0; id[1] = 0; buffer[0] = 0; buffer[1] = 0; xrun[0] = false; xrun[1] = false; bufferPointer[0] = 0; bufferPointer[1] = 0; } -}; - -// Declarations for utility functions, callbacks, and structures -// specific to the DirectSound implementation. -static BOOL CALLBACK deviceQueryCallback( LPGUID lpguid, - LPCTSTR description, - LPCTSTR module, - LPVOID lpContext ); - -static const char* getErrorString( int code ); - -static unsigned __stdcall callbackHandler( void *ptr ); - -struct DsDevice { - LPGUID id[2]; - bool validId[2]; - bool found; - std::string name; - - DsDevice() - : found(false) { validId[0] = false; validId[1] = false; } -}; - -struct DsProbeData { - bool isInput; - std::vector* dsDevices; -}; - -RtApiDs :: RtApiDs() -{ - // Dsound will run both-threaded. If CoInitialize fails, then just - // accept whatever the mainline chose for a threading model. - coInitialized_ = false; - HRESULT hr = CoInitialize( NULL ); - if ( !FAILED( hr ) ) coInitialized_ = true; -} - -RtApiDs :: ~RtApiDs() -{ - if ( stream_.state != STREAM_CLOSED ) closeStream(); - if ( coInitialized_ ) CoUninitialize(); // balanced call. -} - -// The DirectSound default output is always the first device. -unsigned int RtApiDs :: getDefaultOutputDevice( void ) -{ - return 0; -} - -// The DirectSound default input is always the first input device, -// which is the first capture device enumerated. -unsigned int RtApiDs :: getDefaultInputDevice( void ) -{ - return 0; -} - -unsigned int RtApiDs :: getDeviceCount( void ) -{ - // Set query flag for previously found devices to false, so that we - // can check for any devices that have disappeared. - for ( unsigned int i=0; i(dsDevices.size()); -} - -RtAudio::DeviceInfo RtApiDs :: getDeviceInfo( unsigned int device ) -{ - RtAudio::DeviceInfo info; - info.probed = false; - - if ( dsDevices.size() == 0 ) { - // Force a query of all devices - getDeviceCount(); - if ( dsDevices.size() == 0 ) { - errorText_ = "RtApiDs::getDeviceInfo: no devices found!"; - error( RtAudioError::INVALID_USE ); - return info; - } - } - - if ( device >= dsDevices.size() ) { - errorText_ = "RtApiDs::getDeviceInfo: device ID is invalid!"; - error( RtAudioError::INVALID_USE ); - return info; - } - - HRESULT result; - if ( dsDevices[ device ].validId[0] == false ) goto probeInput; - - LPDIRECTSOUND output; - DSCAPS outCaps; - result = DirectSoundCreate( dsDevices[ device ].id[0], &output, NULL ); - if ( FAILED( result ) ) { - errorStream_ << "RtApiDs::getDeviceInfo: error (" << getErrorString( result ) << ") opening output device (" << dsDevices[ device ].name << ")!"; - errorText_ = errorStream_.str(); - error( RtAudioError::WARNING ); - goto probeInput; - } - - outCaps.dwSize = sizeof( outCaps ); - result = output->GetCaps( &outCaps ); - if ( FAILED( result ) ) { - output->Release(); - errorStream_ << "RtApiDs::getDeviceInfo: error (" << getErrorString( result ) << ") getting capabilities!"; - errorText_ = errorStream_.str(); - error( RtAudioError::WARNING ); - goto probeInput; - } - - // Get output channel information. - info.outputChannels = ( outCaps.dwFlags & DSCAPS_PRIMARYSTEREO ) ? 2 : 1; - - // Get sample rate information. - info.sampleRates.clear(); - for ( unsigned int k=0; k= (unsigned int) outCaps.dwMinSecondarySampleRate && - SAMPLE_RATES[k] <= (unsigned int) outCaps.dwMaxSecondarySampleRate ) { - info.sampleRates.push_back( SAMPLE_RATES[k] ); - - if ( !info.preferredSampleRate || ( SAMPLE_RATES[k] <= 48000 && SAMPLE_RATES[k] > info.preferredSampleRate ) ) - info.preferredSampleRate = SAMPLE_RATES[k]; - } - } - - // Get format information. - if ( outCaps.dwFlags & DSCAPS_PRIMARY16BIT ) info.nativeFormats |= RTAUDIO_SINT16; - if ( outCaps.dwFlags & DSCAPS_PRIMARY8BIT ) info.nativeFormats |= RTAUDIO_SINT8; - - output->Release(); - - if ( getDefaultOutputDevice() == device ) - info.isDefaultOutput = true; - - if ( dsDevices[ device ].validId[1] == false ) { - info.name = dsDevices[ device ].name; - info.probed = true; - return info; - } - - probeInput: - - LPDIRECTSOUNDCAPTURE input; - result = DirectSoundCaptureCreate( dsDevices[ device ].id[1], &input, NULL ); - if ( FAILED( result ) ) { - errorStream_ << "RtApiDs::getDeviceInfo: error (" << getErrorString( result ) << ") opening input device (" << dsDevices[ device ].name << ")!"; - errorText_ = errorStream_.str(); - error( RtAudioError::WARNING ); - return info; - } - - DSCCAPS inCaps; - inCaps.dwSize = sizeof( inCaps ); - result = input->GetCaps( &inCaps ); - if ( FAILED( result ) ) { - input->Release(); - errorStream_ << "RtApiDs::getDeviceInfo: error (" << getErrorString( result ) << ") getting object capabilities (" << dsDevices[ device ].name << ")!"; - errorText_ = errorStream_.str(); - error( RtAudioError::WARNING ); - return info; - } - - // Get input channel information. - info.inputChannels = inCaps.dwChannels; - - // Get sample rate and format information. - std::vector rates; - if ( inCaps.dwChannels >= 2 ) { - if ( inCaps.dwFormats & WAVE_FORMAT_1S16 ) info.nativeFormats |= RTAUDIO_SINT16; - if ( inCaps.dwFormats & WAVE_FORMAT_2S16 ) info.nativeFormats |= RTAUDIO_SINT16; - if ( inCaps.dwFormats & WAVE_FORMAT_4S16 ) info.nativeFormats |= RTAUDIO_SINT16; - if ( inCaps.dwFormats & WAVE_FORMAT_96S16 ) info.nativeFormats |= RTAUDIO_SINT16; - if ( inCaps.dwFormats & WAVE_FORMAT_1S08 ) info.nativeFormats |= RTAUDIO_SINT8; - if ( inCaps.dwFormats & WAVE_FORMAT_2S08 ) info.nativeFormats |= RTAUDIO_SINT8; - if ( inCaps.dwFormats & WAVE_FORMAT_4S08 ) info.nativeFormats |= RTAUDIO_SINT8; - if ( inCaps.dwFormats & WAVE_FORMAT_96S08 ) info.nativeFormats |= RTAUDIO_SINT8; - - if ( info.nativeFormats & RTAUDIO_SINT16 ) { - if ( inCaps.dwFormats & WAVE_FORMAT_1S16 ) rates.push_back( 11025 ); - if ( inCaps.dwFormats & WAVE_FORMAT_2S16 ) rates.push_back( 22050 ); - if ( inCaps.dwFormats & WAVE_FORMAT_4S16 ) rates.push_back( 44100 ); - if ( inCaps.dwFormats & WAVE_FORMAT_96S16 ) rates.push_back( 96000 ); - } - else if ( info.nativeFormats & RTAUDIO_SINT8 ) { - if ( inCaps.dwFormats & WAVE_FORMAT_1S08 ) rates.push_back( 11025 ); - if ( inCaps.dwFormats & WAVE_FORMAT_2S08 ) rates.push_back( 22050 ); - if ( inCaps.dwFormats & WAVE_FORMAT_4S08 ) rates.push_back( 44100 ); - if ( inCaps.dwFormats & WAVE_FORMAT_96S08 ) rates.push_back( 96000 ); - } - } - else if ( inCaps.dwChannels == 1 ) { - if ( inCaps.dwFormats & WAVE_FORMAT_1M16 ) info.nativeFormats |= RTAUDIO_SINT16; - if ( inCaps.dwFormats & WAVE_FORMAT_2M16 ) info.nativeFormats |= RTAUDIO_SINT16; - if ( inCaps.dwFormats & WAVE_FORMAT_4M16 ) info.nativeFormats |= RTAUDIO_SINT16; - if ( inCaps.dwFormats & WAVE_FORMAT_96M16 ) info.nativeFormats |= RTAUDIO_SINT16; - if ( inCaps.dwFormats & WAVE_FORMAT_1M08 ) info.nativeFormats |= RTAUDIO_SINT8; - if ( inCaps.dwFormats & WAVE_FORMAT_2M08 ) info.nativeFormats |= RTAUDIO_SINT8; - if ( inCaps.dwFormats & WAVE_FORMAT_4M08 ) info.nativeFormats |= RTAUDIO_SINT8; - if ( inCaps.dwFormats & WAVE_FORMAT_96M08 ) info.nativeFormats |= RTAUDIO_SINT8; - - if ( info.nativeFormats & RTAUDIO_SINT16 ) { - if ( inCaps.dwFormats & WAVE_FORMAT_1M16 ) rates.push_back( 11025 ); - if ( inCaps.dwFormats & WAVE_FORMAT_2M16 ) rates.push_back( 22050 ); - if ( inCaps.dwFormats & WAVE_FORMAT_4M16 ) rates.push_back( 44100 ); - if ( inCaps.dwFormats & WAVE_FORMAT_96M16 ) rates.push_back( 96000 ); - } - else if ( info.nativeFormats & RTAUDIO_SINT8 ) { - if ( inCaps.dwFormats & WAVE_FORMAT_1M08 ) rates.push_back( 11025 ); - if ( inCaps.dwFormats & WAVE_FORMAT_2M08 ) rates.push_back( 22050 ); - if ( inCaps.dwFormats & WAVE_FORMAT_4M08 ) rates.push_back( 44100 ); - if ( inCaps.dwFormats & WAVE_FORMAT_96M08 ) rates.push_back( 96000 ); - } - } - else info.inputChannels = 0; // technically, this would be an error - - input->Release(); - - if ( info.inputChannels == 0 ) return info; - - // Copy the supported rates to the info structure but avoid duplication. - bool found; - for ( unsigned int i=0; i 0 && info.inputChannels > 0 ) - info.duplexChannels = (info.outputChannels > info.inputChannels) ? info.inputChannels : info.outputChannels; - - if ( device == 0 ) info.isDefaultInput = true; - - // Copy name and return. - info.name = dsDevices[ device ].name; - info.probed = true; - return info; -} - -bool RtApiDs :: probeDeviceOpen( unsigned int device, StreamMode mode, unsigned int channels, - unsigned int firstChannel, unsigned int sampleRate, - RtAudioFormat format, unsigned int *bufferSize, - RtAudio::StreamOptions *options ) -{ - if ( channels + firstChannel > 2 ) { - errorText_ = "RtApiDs::probeDeviceOpen: DirectSound does not support more than 2 channels per device."; - return FAILURE; - } - - size_t nDevices = dsDevices.size(); - if ( nDevices == 0 ) { - // This should not happen because a check is made before this function is called. - errorText_ = "RtApiDs::probeDeviceOpen: no devices found!"; - return FAILURE; - } - - if ( device >= nDevices ) { - // This should not happen because a check is made before this function is called. - errorText_ = "RtApiDs::probeDeviceOpen: device ID is invalid!"; - return FAILURE; - } - - if ( mode == OUTPUT ) { - if ( dsDevices[ device ].validId[0] == false ) { - errorStream_ << "RtApiDs::probeDeviceOpen: device (" << device << ") does not support output!"; - errorText_ = errorStream_.str(); - return FAILURE; - } - } - else { // mode == INPUT - if ( dsDevices[ device ].validId[1] == false ) { - errorStream_ << "RtApiDs::probeDeviceOpen: device (" << device << ") does not support input!"; - errorText_ = errorStream_.str(); - return FAILURE; - } - } - - // According to a note in PortAudio, using GetDesktopWindow() - // instead of GetForegroundWindow() is supposed to avoid problems - // that occur when the application's window is not the foreground - // window. Also, if the application window closes before the - // DirectSound buffer, DirectSound can crash. In the past, I had - // problems when using GetDesktopWindow() but it seems fine now - // (January 2010). I'll leave it commented here. - // HWND hWnd = GetForegroundWindow(); - HWND hWnd = GetDesktopWindow(); - - // Check the numberOfBuffers parameter and limit the lowest value to - // two. This is a judgement call and a value of two is probably too - // low for capture, but it should work for playback. - int nBuffers = 0; - if ( options ) nBuffers = options->numberOfBuffers; - if ( options && options->flags & RTAUDIO_MINIMIZE_LATENCY ) nBuffers = 2; - if ( nBuffers < 2 ) nBuffers = 3; - - // Check the lower range of the user-specified buffer size and set - // (arbitrarily) to a lower bound of 32. - if ( *bufferSize < 32 ) *bufferSize = 32; - - // Create the wave format structure. The data format setting will - // be determined later. - WAVEFORMATEX waveFormat; - ZeroMemory( &waveFormat, sizeof(WAVEFORMATEX) ); - waveFormat.wFormatTag = WAVE_FORMAT_PCM; - waveFormat.nChannels = channels + firstChannel; - waveFormat.nSamplesPerSec = (unsigned long) sampleRate; - - // Determine the device buffer size. By default, we'll use the value - // defined above (32K), but we will grow it to make allowances for - // very large software buffer sizes. - DWORD dsBufferSize = MINIMUM_DEVICE_BUFFER_SIZE; - DWORD dsPointerLeadTime = 0; - - void *ohandle = 0, *bhandle = 0; - HRESULT result; - if ( mode == OUTPUT ) { - - LPDIRECTSOUND output; - result = DirectSoundCreate( dsDevices[ device ].id[0], &output, NULL ); - if ( FAILED( result ) ) { - errorStream_ << "RtApiDs::probeDeviceOpen: error (" << getErrorString( result ) << ") opening output device (" << dsDevices[ device ].name << ")!"; - errorText_ = errorStream_.str(); - return FAILURE; - } - - DSCAPS outCaps; - outCaps.dwSize = sizeof( outCaps ); - result = output->GetCaps( &outCaps ); - if ( FAILED( result ) ) { - output->Release(); - errorStream_ << "RtApiDs::probeDeviceOpen: error (" << getErrorString( result ) << ") getting capabilities (" << dsDevices[ device ].name << ")!"; - errorText_ = errorStream_.str(); - return FAILURE; - } - - // Check channel information. - if ( channels + firstChannel == 2 && !( outCaps.dwFlags & DSCAPS_PRIMARYSTEREO ) ) { - errorStream_ << "RtApiDs::getDeviceInfo: the output device (" << dsDevices[ device ].name << ") does not support stereo playback."; - errorText_ = errorStream_.str(); - return FAILURE; - } - - // Check format information. Use 16-bit format unless not - // supported or user requests 8-bit. - if ( outCaps.dwFlags & DSCAPS_PRIMARY16BIT && - !( format == RTAUDIO_SINT8 && outCaps.dwFlags & DSCAPS_PRIMARY8BIT ) ) { - waveFormat.wBitsPerSample = 16; - stream_.deviceFormat[mode] = RTAUDIO_SINT16; - } - else { - waveFormat.wBitsPerSample = 8; - stream_.deviceFormat[mode] = RTAUDIO_SINT8; - } - stream_.userFormat = format; - - // Update wave format structure and buffer information. - waveFormat.nBlockAlign = waveFormat.nChannels * waveFormat.wBitsPerSample / 8; - waveFormat.nAvgBytesPerSec = waveFormat.nSamplesPerSec * waveFormat.nBlockAlign; - dsPointerLeadTime = nBuffers * (*bufferSize) * (waveFormat.wBitsPerSample / 8) * channels; - - // If the user wants an even bigger buffer, increase the device buffer size accordingly. - while ( dsPointerLeadTime * 2U > dsBufferSize ) - dsBufferSize *= 2; - - // Set cooperative level to DSSCL_EXCLUSIVE ... sound stops when window focus changes. - // result = output->SetCooperativeLevel( hWnd, DSSCL_EXCLUSIVE ); - // Set cooperative level to DSSCL_PRIORITY ... sound remains when window focus changes. - result = output->SetCooperativeLevel( hWnd, DSSCL_PRIORITY ); - if ( FAILED( result ) ) { - output->Release(); - errorStream_ << "RtApiDs::probeDeviceOpen: error (" << getErrorString( result ) << ") setting cooperative level (" << dsDevices[ device ].name << ")!"; - errorText_ = errorStream_.str(); - return FAILURE; - } - - // Even though we will write to the secondary buffer, we need to - // access the primary buffer to set the correct output format - // (since the default is 8-bit, 22 kHz!). Setup the DS primary - // buffer description. - DSBUFFERDESC bufferDescription; - ZeroMemory( &bufferDescription, sizeof( DSBUFFERDESC ) ); - bufferDescription.dwSize = sizeof( DSBUFFERDESC ); - bufferDescription.dwFlags = DSBCAPS_PRIMARYBUFFER; - - // Obtain the primary buffer - LPDIRECTSOUNDBUFFER buffer; - result = output->CreateSoundBuffer( &bufferDescription, &buffer, NULL ); - if ( FAILED( result ) ) { - output->Release(); - errorStream_ << "RtApiDs::probeDeviceOpen: error (" << getErrorString( result ) << ") accessing primary buffer (" << dsDevices[ device ].name << ")!"; - errorText_ = errorStream_.str(); - return FAILURE; - } - - // Set the primary DS buffer sound format. - result = buffer->SetFormat( &waveFormat ); - if ( FAILED( result ) ) { - output->Release(); - errorStream_ << "RtApiDs::probeDeviceOpen: error (" << getErrorString( result ) << ") setting primary buffer format (" << dsDevices[ device ].name << ")!"; - errorText_ = errorStream_.str(); - return FAILURE; - } - - // Setup the secondary DS buffer description. - ZeroMemory( &bufferDescription, sizeof( DSBUFFERDESC ) ); - bufferDescription.dwSize = sizeof( DSBUFFERDESC ); - bufferDescription.dwFlags = ( DSBCAPS_STICKYFOCUS | - DSBCAPS_GLOBALFOCUS | - DSBCAPS_GETCURRENTPOSITION2 | - DSBCAPS_LOCHARDWARE ); // Force hardware mixing - bufferDescription.dwBufferBytes = dsBufferSize; - bufferDescription.lpwfxFormat = &waveFormat; - - // Try to create the secondary DS buffer. If that doesn't work, - // try to use software mixing. Otherwise, there's a problem. - result = output->CreateSoundBuffer( &bufferDescription, &buffer, NULL ); - if ( FAILED( result ) ) { - bufferDescription.dwFlags = ( DSBCAPS_STICKYFOCUS | - DSBCAPS_GLOBALFOCUS | - DSBCAPS_GETCURRENTPOSITION2 | - DSBCAPS_LOCSOFTWARE ); // Force software mixing - result = output->CreateSoundBuffer( &bufferDescription, &buffer, NULL ); - if ( FAILED( result ) ) { - output->Release(); - errorStream_ << "RtApiDs::probeDeviceOpen: error (" << getErrorString( result ) << ") creating secondary buffer (" << dsDevices[ device ].name << ")!"; - errorText_ = errorStream_.str(); - return FAILURE; - } - } - - // Get the buffer size ... might be different from what we specified. - DSBCAPS dsbcaps; - dsbcaps.dwSize = sizeof( DSBCAPS ); - result = buffer->GetCaps( &dsbcaps ); - if ( FAILED( result ) ) { - output->Release(); - buffer->Release(); - errorStream_ << "RtApiDs::probeDeviceOpen: error (" << getErrorString( result ) << ") getting buffer settings (" << dsDevices[ device ].name << ")!"; - errorText_ = errorStream_.str(); - return FAILURE; - } - - dsBufferSize = dsbcaps.dwBufferBytes; - - // Lock the DS buffer - LPVOID audioPtr; - DWORD dataLen; - result = buffer->Lock( 0, dsBufferSize, &audioPtr, &dataLen, NULL, NULL, 0 ); - if ( FAILED( result ) ) { - output->Release(); - buffer->Release(); - errorStream_ << "RtApiDs::probeDeviceOpen: error (" << getErrorString( result ) << ") locking buffer (" << dsDevices[ device ].name << ")!"; - errorText_ = errorStream_.str(); - return FAILURE; - } - - // Zero the DS buffer - ZeroMemory( audioPtr, dataLen ); - - // Unlock the DS buffer - result = buffer->Unlock( audioPtr, dataLen, NULL, 0 ); - if ( FAILED( result ) ) { - output->Release(); - buffer->Release(); - errorStream_ << "RtApiDs::probeDeviceOpen: error (" << getErrorString( result ) << ") unlocking buffer (" << dsDevices[ device ].name << ")!"; - errorText_ = errorStream_.str(); - return FAILURE; - } - - ohandle = (void *) output; - bhandle = (void *) buffer; - } - - if ( mode == INPUT ) { - - LPDIRECTSOUNDCAPTURE input; - result = DirectSoundCaptureCreate( dsDevices[ device ].id[1], &input, NULL ); - if ( FAILED( result ) ) { - errorStream_ << "RtApiDs::probeDeviceOpen: error (" << getErrorString( result ) << ") opening input device (" << dsDevices[ device ].name << ")!"; - errorText_ = errorStream_.str(); - return FAILURE; - } - - DSCCAPS inCaps; - inCaps.dwSize = sizeof( inCaps ); - result = input->GetCaps( &inCaps ); - if ( FAILED( result ) ) { - input->Release(); - errorStream_ << "RtApiDs::probeDeviceOpen: error (" << getErrorString( result ) << ") getting input capabilities (" << dsDevices[ device ].name << ")!"; - errorText_ = errorStream_.str(); - return FAILURE; - } - - // Check channel information. - if ( inCaps.dwChannels < channels + firstChannel ) { - errorText_ = "RtApiDs::getDeviceInfo: the input device does not support requested input channels."; - return FAILURE; - } - - // Check format information. Use 16-bit format unless user - // requests 8-bit. - DWORD deviceFormats; - if ( channels + firstChannel == 2 ) { - deviceFormats = WAVE_FORMAT_1S08 | WAVE_FORMAT_2S08 | WAVE_FORMAT_4S08 | WAVE_FORMAT_96S08; - if ( format == RTAUDIO_SINT8 && inCaps.dwFormats & deviceFormats ) { - waveFormat.wBitsPerSample = 8; - stream_.deviceFormat[mode] = RTAUDIO_SINT8; - } - else { // assume 16-bit is supported - waveFormat.wBitsPerSample = 16; - stream_.deviceFormat[mode] = RTAUDIO_SINT16; - } - } - else { // channel == 1 - deviceFormats = WAVE_FORMAT_1M08 | WAVE_FORMAT_2M08 | WAVE_FORMAT_4M08 | WAVE_FORMAT_96M08; - if ( format == RTAUDIO_SINT8 && inCaps.dwFormats & deviceFormats ) { - waveFormat.wBitsPerSample = 8; - stream_.deviceFormat[mode] = RTAUDIO_SINT8; - } - else { // assume 16-bit is supported - waveFormat.wBitsPerSample = 16; - stream_.deviceFormat[mode] = RTAUDIO_SINT16; - } - } - stream_.userFormat = format; - - // Update wave format structure and buffer information. - waveFormat.nBlockAlign = waveFormat.nChannels * waveFormat.wBitsPerSample / 8; - waveFormat.nAvgBytesPerSec = waveFormat.nSamplesPerSec * waveFormat.nBlockAlign; - dsPointerLeadTime = nBuffers * (*bufferSize) * (waveFormat.wBitsPerSample / 8) * channels; - - // If the user wants an even bigger buffer, increase the device buffer size accordingly. - while ( dsPointerLeadTime * 2U > dsBufferSize ) - dsBufferSize *= 2; - - // Setup the secondary DS buffer description. - DSCBUFFERDESC bufferDescription; - ZeroMemory( &bufferDescription, sizeof( DSCBUFFERDESC ) ); - bufferDescription.dwSize = sizeof( DSCBUFFERDESC ); - bufferDescription.dwFlags = 0; - bufferDescription.dwReserved = 0; - bufferDescription.dwBufferBytes = dsBufferSize; - bufferDescription.lpwfxFormat = &waveFormat; - - // Create the capture buffer. - LPDIRECTSOUNDCAPTUREBUFFER buffer; - result = input->CreateCaptureBuffer( &bufferDescription, &buffer, NULL ); - if ( FAILED( result ) ) { - input->Release(); - errorStream_ << "RtApiDs::probeDeviceOpen: error (" << getErrorString( result ) << ") creating input buffer (" << dsDevices[ device ].name << ")!"; - errorText_ = errorStream_.str(); - return FAILURE; - } - - // Get the buffer size ... might be different from what we specified. - DSCBCAPS dscbcaps; - dscbcaps.dwSize = sizeof( DSCBCAPS ); - result = buffer->GetCaps( &dscbcaps ); - if ( FAILED( result ) ) { - input->Release(); - buffer->Release(); - errorStream_ << "RtApiDs::probeDeviceOpen: error (" << getErrorString( result ) << ") getting buffer settings (" << dsDevices[ device ].name << ")!"; - errorText_ = errorStream_.str(); - return FAILURE; - } - - dsBufferSize = dscbcaps.dwBufferBytes; - - // NOTE: We could have a problem here if this is a duplex stream - // and the play and capture hardware buffer sizes are different - // (I'm actually not sure if that is a problem or not). - // Currently, we are not verifying that. - - // Lock the capture buffer - LPVOID audioPtr; - DWORD dataLen; - result = buffer->Lock( 0, dsBufferSize, &audioPtr, &dataLen, NULL, NULL, 0 ); - if ( FAILED( result ) ) { - input->Release(); - buffer->Release(); - errorStream_ << "RtApiDs::probeDeviceOpen: error (" << getErrorString( result ) << ") locking input buffer (" << dsDevices[ device ].name << ")!"; - errorText_ = errorStream_.str(); - return FAILURE; - } - - // Zero the buffer - ZeroMemory( audioPtr, dataLen ); - - // Unlock the buffer - result = buffer->Unlock( audioPtr, dataLen, NULL, 0 ); - if ( FAILED( result ) ) { - input->Release(); - buffer->Release(); - errorStream_ << "RtApiDs::probeDeviceOpen: error (" << getErrorString( result ) << ") unlocking input buffer (" << dsDevices[ device ].name << ")!"; - errorText_ = errorStream_.str(); - return FAILURE; - } - - ohandle = (void *) input; - bhandle = (void *) buffer; - } - - // Set various stream parameters - DsHandle *handle = 0; - stream_.nDeviceChannels[mode] = channels + firstChannel; - stream_.nUserChannels[mode] = channels; - stream_.bufferSize = *bufferSize; - stream_.channelOffset[mode] = firstChannel; - stream_.deviceInterleaved[mode] = true; - if ( options && options->flags & RTAUDIO_NONINTERLEAVED ) stream_.userInterleaved = false; - else stream_.userInterleaved = true; - - // Set flag for buffer conversion - stream_.doConvertBuffer[mode] = false; - if (stream_.nUserChannels[mode] != stream_.nDeviceChannels[mode]) - stream_.doConvertBuffer[mode] = true; - if (stream_.userFormat != stream_.deviceFormat[mode]) - stream_.doConvertBuffer[mode] = true; - if ( stream_.userInterleaved != stream_.deviceInterleaved[mode] && - stream_.nUserChannels[mode] > 1 ) - stream_.doConvertBuffer[mode] = true; - - // Allocate necessary internal buffers - long bufferBytes = stream_.nUserChannels[mode] * *bufferSize * formatBytes( stream_.userFormat ); - stream_.userBuffer[mode] = (char *) calloc( bufferBytes, 1 ); - if ( stream_.userBuffer[mode] == NULL ) { - errorText_ = "RtApiDs::probeDeviceOpen: error allocating user buffer memory."; - goto error; - } - - if ( stream_.doConvertBuffer[mode] ) { - - bool makeBuffer = true; - bufferBytes = stream_.nDeviceChannels[mode] * formatBytes( stream_.deviceFormat[mode] ); - if ( mode == INPUT ) { - if ( stream_.mode == OUTPUT && stream_.deviceBuffer ) { - unsigned long bytesOut = stream_.nDeviceChannels[0] * formatBytes( stream_.deviceFormat[0] ); - if ( bufferBytes <= (long) bytesOut ) makeBuffer = false; - } - } - - if ( makeBuffer ) { - bufferBytes *= *bufferSize; - if ( stream_.deviceBuffer ) free( stream_.deviceBuffer ); - stream_.deviceBuffer = (char *) calloc( bufferBytes, 1 ); - if ( stream_.deviceBuffer == NULL ) { - errorText_ = "RtApiDs::probeDeviceOpen: error allocating device buffer memory."; - goto error; - } - } - } - - // Allocate our DsHandle structures for the stream. - if ( stream_.apiHandle == 0 ) { - try { - handle = new DsHandle; - } - catch ( std::bad_alloc& ) { - errorText_ = "RtApiDs::probeDeviceOpen: error allocating AsioHandle memory."; - goto error; - } - - // Create a manual-reset event. - handle->condition = CreateEvent( NULL, // no security - TRUE, // manual-reset - FALSE, // non-signaled initially - NULL ); // unnamed - stream_.apiHandle = (void *) handle; - } - else - handle = (DsHandle *) stream_.apiHandle; - handle->id[mode] = ohandle; - handle->buffer[mode] = bhandle; - handle->dsBufferSize[mode] = dsBufferSize; - handle->dsPointerLeadTime[mode] = dsPointerLeadTime; - - stream_.device[mode] = device; - stream_.state = STREAM_STOPPED; - if ( stream_.mode == OUTPUT && mode == INPUT ) - // We had already set up an output stream. - stream_.mode = DUPLEX; - else - stream_.mode = mode; - stream_.nBuffers = nBuffers; - stream_.sampleRate = sampleRate; - - // Setup the buffer conversion information structure. - if ( stream_.doConvertBuffer[mode] ) setConvertInfo( mode, firstChannel ); - - // Setup the callback thread. - if ( stream_.callbackInfo.isRunning == false ) { - unsigned threadId; - stream_.callbackInfo.isRunning = true; - stream_.callbackInfo.object = (void *) this; - stream_.callbackInfo.thread = _beginthreadex( NULL, 0, &callbackHandler, - &stream_.callbackInfo, 0, &threadId ); - if ( stream_.callbackInfo.thread == 0 ) { - errorText_ = "RtApiDs::probeDeviceOpen: error creating callback thread!"; - goto error; - } - - // Boost DS thread priority - SetThreadPriority( (HANDLE) stream_.callbackInfo.thread, THREAD_PRIORITY_HIGHEST ); - } - return SUCCESS; - - error: - if ( handle ) { - if ( handle->buffer[0] ) { // the object pointer can be NULL and valid - LPDIRECTSOUND object = (LPDIRECTSOUND) handle->id[0]; - LPDIRECTSOUNDBUFFER buffer = (LPDIRECTSOUNDBUFFER) handle->buffer[0]; - if ( buffer ) buffer->Release(); - object->Release(); - } - if ( handle->buffer[1] ) { - LPDIRECTSOUNDCAPTURE object = (LPDIRECTSOUNDCAPTURE) handle->id[1]; - LPDIRECTSOUNDCAPTUREBUFFER buffer = (LPDIRECTSOUNDCAPTUREBUFFER) handle->buffer[1]; - if ( buffer ) buffer->Release(); - object->Release(); - } - CloseHandle( handle->condition ); - delete handle; - stream_.apiHandle = 0; - } - - for ( int i=0; i<2; i++ ) { - if ( stream_.userBuffer[i] ) { - free( stream_.userBuffer[i] ); - stream_.userBuffer[i] = 0; - } - } - - if ( stream_.deviceBuffer ) { - free( stream_.deviceBuffer ); - stream_.deviceBuffer = 0; - } - - stream_.state = STREAM_CLOSED; - return FAILURE; -} - -void RtApiDs :: closeStream() -{ - if ( stream_.state == STREAM_CLOSED ) { - errorText_ = "RtApiDs::closeStream(): no open stream to close!"; - error( RtAudioError::WARNING ); - return; - } - - // Stop the callback thread. - stream_.callbackInfo.isRunning = false; - WaitForSingleObject( (HANDLE) stream_.callbackInfo.thread, INFINITE ); - CloseHandle( (HANDLE) stream_.callbackInfo.thread ); - - DsHandle *handle = (DsHandle *) stream_.apiHandle; - if ( handle ) { - if ( handle->buffer[0] ) { // the object pointer can be NULL and valid - LPDIRECTSOUND object = (LPDIRECTSOUND) handle->id[0]; - LPDIRECTSOUNDBUFFER buffer = (LPDIRECTSOUNDBUFFER) handle->buffer[0]; - if ( buffer ) { - buffer->Stop(); - buffer->Release(); - } - object->Release(); - } - if ( handle->buffer[1] ) { - LPDIRECTSOUNDCAPTURE object = (LPDIRECTSOUNDCAPTURE) handle->id[1]; - LPDIRECTSOUNDCAPTUREBUFFER buffer = (LPDIRECTSOUNDCAPTUREBUFFER) handle->buffer[1]; - if ( buffer ) { - buffer->Stop(); - buffer->Release(); - } - object->Release(); - } - CloseHandle( handle->condition ); - delete handle; - stream_.apiHandle = 0; - } - - for ( int i=0; i<2; i++ ) { - if ( stream_.userBuffer[i] ) { - free( stream_.userBuffer[i] ); - stream_.userBuffer[i] = 0; - } - } - - if ( stream_.deviceBuffer ) { - free( stream_.deviceBuffer ); - stream_.deviceBuffer = 0; - } - - stream_.mode = UNINITIALIZED; - stream_.state = STREAM_CLOSED; -} - -void RtApiDs :: startStream() -{ - verifyStream(); - if ( stream_.state == STREAM_RUNNING ) { - errorText_ = "RtApiDs::startStream(): the stream is already running!"; - error( RtAudioError::WARNING ); - return; - } - - #if defined( HAVE_GETTIMEOFDAY ) - gettimeofday( &stream_.lastTickTimestamp, NULL ); - #endif - - DsHandle *handle = (DsHandle *) stream_.apiHandle; - - // Increase scheduler frequency on lesser windows (a side-effect of - // increasing timer accuracy). On greater windows (Win2K or later), - // this is already in effect. - timeBeginPeriod( 1 ); - - buffersRolling = false; - duplexPrerollBytes = 0; - - if ( stream_.mode == DUPLEX ) { - // 0.5 seconds of silence in DUPLEX mode while the devices spin up and synchronize. - duplexPrerollBytes = (int) ( 0.5 * stream_.sampleRate * formatBytes( stream_.deviceFormat[1] ) * stream_.nDeviceChannels[1] ); - } - - HRESULT result = 0; - if ( stream_.mode == OUTPUT || stream_.mode == DUPLEX ) { - - LPDIRECTSOUNDBUFFER buffer = (LPDIRECTSOUNDBUFFER) handle->buffer[0]; - result = buffer->Play( 0, 0, DSBPLAY_LOOPING ); - if ( FAILED( result ) ) { - errorStream_ << "RtApiDs::startStream: error (" << getErrorString( result ) << ") starting output buffer!"; - errorText_ = errorStream_.str(); - goto unlock; - } - } - - if ( stream_.mode == INPUT || stream_.mode == DUPLEX ) { - - LPDIRECTSOUNDCAPTUREBUFFER buffer = (LPDIRECTSOUNDCAPTUREBUFFER) handle->buffer[1]; - result = buffer->Start( DSCBSTART_LOOPING ); - if ( FAILED( result ) ) { - errorStream_ << "RtApiDs::startStream: error (" << getErrorString( result ) << ") starting input buffer!"; - errorText_ = errorStream_.str(); - goto unlock; - } - } - - handle->drainCounter = 0; - handle->internalDrain = false; - ResetEvent( handle->condition ); - stream_.state = STREAM_RUNNING; - - unlock: - if ( FAILED( result ) ) error( RtAudioError::SYSTEM_ERROR ); -} - -void RtApiDs :: stopStream() -{ - verifyStream(); - if ( stream_.state == STREAM_STOPPED ) { - errorText_ = "RtApiDs::stopStream(): the stream is already stopped!"; - error( RtAudioError::WARNING ); - return; - } - - HRESULT result = 0; - LPVOID audioPtr; - DWORD dataLen; - DsHandle *handle = (DsHandle *) stream_.apiHandle; - if ( stream_.mode == OUTPUT || stream_.mode == DUPLEX ) { - if ( handle->drainCounter == 0 ) { - handle->drainCounter = 2; - WaitForSingleObject( handle->condition, INFINITE ); // block until signaled - } - - stream_.state = STREAM_STOPPED; - - MUTEX_LOCK( &stream_.mutex ); - - // Stop the buffer and clear memory - LPDIRECTSOUNDBUFFER buffer = (LPDIRECTSOUNDBUFFER) handle->buffer[0]; - result = buffer->Stop(); - if ( FAILED( result ) ) { - errorStream_ << "RtApiDs::stopStream: error (" << getErrorString( result ) << ") stopping output buffer!"; - errorText_ = errorStream_.str(); - goto unlock; - } - - // Lock the buffer and clear it so that if we start to play again, - // we won't have old data playing. - result = buffer->Lock( 0, handle->dsBufferSize[0], &audioPtr, &dataLen, NULL, NULL, 0 ); - if ( FAILED( result ) ) { - errorStream_ << "RtApiDs::stopStream: error (" << getErrorString( result ) << ") locking output buffer!"; - errorText_ = errorStream_.str(); - goto unlock; - } - - // Zero the DS buffer - ZeroMemory( audioPtr, dataLen ); - - // Unlock the DS buffer - result = buffer->Unlock( audioPtr, dataLen, NULL, 0 ); - if ( FAILED( result ) ) { - errorStream_ << "RtApiDs::stopStream: error (" << getErrorString( result ) << ") unlocking output buffer!"; - errorText_ = errorStream_.str(); - goto unlock; - } - - // If we start playing again, we must begin at beginning of buffer. - handle->bufferPointer[0] = 0; - } - - if ( stream_.mode == INPUT || stream_.mode == DUPLEX ) { - LPDIRECTSOUNDCAPTUREBUFFER buffer = (LPDIRECTSOUNDCAPTUREBUFFER) handle->buffer[1]; - audioPtr = NULL; - dataLen = 0; - - stream_.state = STREAM_STOPPED; - - if ( stream_.mode != DUPLEX ) - MUTEX_LOCK( &stream_.mutex ); - - result = buffer->Stop(); - if ( FAILED( result ) ) { - errorStream_ << "RtApiDs::stopStream: error (" << getErrorString( result ) << ") stopping input buffer!"; - errorText_ = errorStream_.str(); - goto unlock; - } - - // Lock the buffer and clear it so that if we start to play again, - // we won't have old data playing. - result = buffer->Lock( 0, handle->dsBufferSize[1], &audioPtr, &dataLen, NULL, NULL, 0 ); - if ( FAILED( result ) ) { - errorStream_ << "RtApiDs::stopStream: error (" << getErrorString( result ) << ") locking input buffer!"; - errorText_ = errorStream_.str(); - goto unlock; - } - - // Zero the DS buffer - ZeroMemory( audioPtr, dataLen ); - - // Unlock the DS buffer - result = buffer->Unlock( audioPtr, dataLen, NULL, 0 ); - if ( FAILED( result ) ) { - errorStream_ << "RtApiDs::stopStream: error (" << getErrorString( result ) << ") unlocking input buffer!"; - errorText_ = errorStream_.str(); - goto unlock; - } - - // If we start recording again, we must begin at beginning of buffer. - handle->bufferPointer[1] = 0; - } - - unlock: - timeEndPeriod( 1 ); // revert to normal scheduler frequency on lesser windows. - MUTEX_UNLOCK( &stream_.mutex ); - - if ( FAILED( result ) ) error( RtAudioError::SYSTEM_ERROR ); -} - -void RtApiDs :: abortStream() -{ - verifyStream(); - if ( stream_.state == STREAM_STOPPED ) { - errorText_ = "RtApiDs::abortStream(): the stream is already stopped!"; - error( RtAudioError::WARNING ); - return; - } - - DsHandle *handle = (DsHandle *) stream_.apiHandle; - handle->drainCounter = 2; - - stopStream(); -} - -void RtApiDs :: callbackEvent() -{ - if ( stream_.state == STREAM_STOPPED || stream_.state == STREAM_STOPPING ) { - Sleep( 50 ); // sleep 50 milliseconds - return; - } - - if ( stream_.state == STREAM_CLOSED ) { - errorText_ = "RtApiDs::callbackEvent(): the stream is closed ... this shouldn't happen!"; - error( RtAudioError::WARNING ); - return; - } - - CallbackInfo *info = (CallbackInfo *) &stream_.callbackInfo; - DsHandle *handle = (DsHandle *) stream_.apiHandle; - - // Check if we were draining the stream and signal is finished. - if ( handle->drainCounter > stream_.nBuffers + 2 ) { - - stream_.state = STREAM_STOPPING; - if ( handle->internalDrain == false ) - SetEvent( handle->condition ); - else - stopStream(); - return; - } - - // Invoke user callback to get fresh output data UNLESS we are - // draining stream. - if ( handle->drainCounter == 0 ) { - RtAudioCallback callback = (RtAudioCallback) info->callback; - double streamTime = getStreamTime(); - RtAudioStreamStatus status = 0; - if ( stream_.mode != INPUT && handle->xrun[0] == true ) { - status |= RTAUDIO_OUTPUT_UNDERFLOW; - handle->xrun[0] = false; - } - if ( stream_.mode != OUTPUT && handle->xrun[1] == true ) { - status |= RTAUDIO_INPUT_OVERFLOW; - handle->xrun[1] = false; - } - int cbReturnValue = callback( stream_.userBuffer[0], stream_.userBuffer[1], - stream_.bufferSize, streamTime, status, info->userData ); - if ( cbReturnValue == 2 ) { - stream_.state = STREAM_STOPPING; - handle->drainCounter = 2; - abortStream(); - return; - } - else if ( cbReturnValue == 1 ) { - handle->drainCounter = 1; - handle->internalDrain = true; - } - } - - HRESULT result; - DWORD currentWritePointer, safeWritePointer; - DWORD currentReadPointer, safeReadPointer; - UINT nextWritePointer; - - LPVOID buffer1 = NULL; - LPVOID buffer2 = NULL; - DWORD bufferSize1 = 0; - DWORD bufferSize2 = 0; - - char *buffer; - long bufferBytes; - - MUTEX_LOCK( &stream_.mutex ); - if ( stream_.state == STREAM_STOPPED ) { - MUTEX_UNLOCK( &stream_.mutex ); - return; - } - - if ( buffersRolling == false ) { - if ( stream_.mode == DUPLEX ) { - //assert( handle->dsBufferSize[0] == handle->dsBufferSize[1] ); - - // It takes a while for the devices to get rolling. As a result, - // there's no guarantee that the capture and write device pointers - // will move in lockstep. Wait here for both devices to start - // rolling, and then set our buffer pointers accordingly. - // e.g. Crystal Drivers: the capture buffer starts up 5700 to 9600 - // bytes later than the write buffer. - - // Stub: a serious risk of having a pre-emptive scheduling round - // take place between the two GetCurrentPosition calls... but I'm - // really not sure how to solve the problem. Temporarily boost to - // Realtime priority, maybe; but I'm not sure what priority the - // DirectSound service threads run at. We *should* be roughly - // within a ms or so of correct. - - LPDIRECTSOUNDBUFFER dsWriteBuffer = (LPDIRECTSOUNDBUFFER) handle->buffer[0]; - LPDIRECTSOUNDCAPTUREBUFFER dsCaptureBuffer = (LPDIRECTSOUNDCAPTUREBUFFER) handle->buffer[1]; - - DWORD startSafeWritePointer, startSafeReadPointer; - - result = dsWriteBuffer->GetCurrentPosition( NULL, &startSafeWritePointer ); - if ( FAILED( result ) ) { - errorStream_ << "RtApiDs::callbackEvent: error (" << getErrorString( result ) << ") getting current write position!"; - errorText_ = errorStream_.str(); - MUTEX_UNLOCK( &stream_.mutex ); - error( RtAudioError::SYSTEM_ERROR ); - return; - } - result = dsCaptureBuffer->GetCurrentPosition( NULL, &startSafeReadPointer ); - if ( FAILED( result ) ) { - errorStream_ << "RtApiDs::callbackEvent: error (" << getErrorString( result ) << ") getting current read position!"; - errorText_ = errorStream_.str(); - MUTEX_UNLOCK( &stream_.mutex ); - error( RtAudioError::SYSTEM_ERROR ); - return; - } - while ( true ) { - result = dsWriteBuffer->GetCurrentPosition( NULL, &safeWritePointer ); - if ( FAILED( result ) ) { - errorStream_ << "RtApiDs::callbackEvent: error (" << getErrorString( result ) << ") getting current write position!"; - errorText_ = errorStream_.str(); - MUTEX_UNLOCK( &stream_.mutex ); - error( RtAudioError::SYSTEM_ERROR ); - return; - } - result = dsCaptureBuffer->GetCurrentPosition( NULL, &safeReadPointer ); - if ( FAILED( result ) ) { - errorStream_ << "RtApiDs::callbackEvent: error (" << getErrorString( result ) << ") getting current read position!"; - errorText_ = errorStream_.str(); - MUTEX_UNLOCK( &stream_.mutex ); - error( RtAudioError::SYSTEM_ERROR ); - return; - } - if ( safeWritePointer != startSafeWritePointer && safeReadPointer != startSafeReadPointer ) break; - Sleep( 1 ); - } - - //assert( handle->dsBufferSize[0] == handle->dsBufferSize[1] ); - - handle->bufferPointer[0] = safeWritePointer + handle->dsPointerLeadTime[0]; - if ( handle->bufferPointer[0] >= handle->dsBufferSize[0] ) handle->bufferPointer[0] -= handle->dsBufferSize[0]; - handle->bufferPointer[1] = safeReadPointer; - } - else if ( stream_.mode == OUTPUT ) { - - // Set the proper nextWritePosition after initial startup. - LPDIRECTSOUNDBUFFER dsWriteBuffer = (LPDIRECTSOUNDBUFFER) handle->buffer[0]; - result = dsWriteBuffer->GetCurrentPosition( ¤tWritePointer, &safeWritePointer ); - if ( FAILED( result ) ) { - errorStream_ << "RtApiDs::callbackEvent: error (" << getErrorString( result ) << ") getting current write position!"; - errorText_ = errorStream_.str(); - MUTEX_UNLOCK( &stream_.mutex ); - error( RtAudioError::SYSTEM_ERROR ); - return; - } - handle->bufferPointer[0] = safeWritePointer + handle->dsPointerLeadTime[0]; - if ( handle->bufferPointer[0] >= handle->dsBufferSize[0] ) handle->bufferPointer[0] -= handle->dsBufferSize[0]; - } - - buffersRolling = true; - } - - if ( stream_.mode == OUTPUT || stream_.mode == DUPLEX ) { - - LPDIRECTSOUNDBUFFER dsBuffer = (LPDIRECTSOUNDBUFFER) handle->buffer[0]; - - if ( handle->drainCounter > 1 ) { // write zeros to the output stream - bufferBytes = stream_.bufferSize * stream_.nUserChannels[0]; - bufferBytes *= formatBytes( stream_.userFormat ); - memset( stream_.userBuffer[0], 0, bufferBytes ); - } - - // Setup parameters and do buffer conversion if necessary. - if ( stream_.doConvertBuffer[0] ) { - buffer = stream_.deviceBuffer; - convertBuffer( buffer, stream_.userBuffer[0], stream_.convertInfo[0] ); - bufferBytes = stream_.bufferSize * stream_.nDeviceChannels[0]; - bufferBytes *= formatBytes( stream_.deviceFormat[0] ); - } - else { - buffer = stream_.userBuffer[0]; - bufferBytes = stream_.bufferSize * stream_.nUserChannels[0]; - bufferBytes *= formatBytes( stream_.userFormat ); - } - - // No byte swapping necessary in DirectSound implementation. - - // Ahhh ... windoze. 16-bit data is signed but 8-bit data is - // unsigned. So, we need to convert our signed 8-bit data here to - // unsigned. - if ( stream_.deviceFormat[0] == RTAUDIO_SINT8 ) - for ( int i=0; idsBufferSize[0]; - nextWritePointer = handle->bufferPointer[0]; - - DWORD endWrite, leadPointer; - while ( true ) { - // Find out where the read and "safe write" pointers are. - result = dsBuffer->GetCurrentPosition( ¤tWritePointer, &safeWritePointer ); - if ( FAILED( result ) ) { - errorStream_ << "RtApiDs::callbackEvent: error (" << getErrorString( result ) << ") getting current write position!"; - errorText_ = errorStream_.str(); - MUTEX_UNLOCK( &stream_.mutex ); - error( RtAudioError::SYSTEM_ERROR ); - return; - } - - // We will copy our output buffer into the region between - // safeWritePointer and leadPointer. If leadPointer is not - // beyond the next endWrite position, wait until it is. - leadPointer = safeWritePointer + handle->dsPointerLeadTime[0]; - //std::cout << "safeWritePointer = " << safeWritePointer << ", leadPointer = " << leadPointer << ", nextWritePointer = " << nextWritePointer << std::endl; - if ( leadPointer > dsBufferSize ) leadPointer -= dsBufferSize; - if ( leadPointer < nextWritePointer ) leadPointer += dsBufferSize; // unwrap offset - endWrite = nextWritePointer + bufferBytes; - - // Check whether the entire write region is behind the play pointer. - if ( leadPointer >= endWrite ) break; - - // If we are here, then we must wait until the leadPointer advances - // beyond the end of our next write region. We use the - // Sleep() function to suspend operation until that happens. - double millis = ( endWrite - leadPointer ) * 1000.0; - millis /= ( formatBytes( stream_.deviceFormat[0]) * stream_.nDeviceChannels[0] * stream_.sampleRate); - if ( millis < 1.0 ) millis = 1.0; - Sleep( (DWORD) millis ); - } - - if ( dsPointerBetween( nextWritePointer, safeWritePointer, currentWritePointer, dsBufferSize ) - || dsPointerBetween( endWrite, safeWritePointer, currentWritePointer, dsBufferSize ) ) { - // We've strayed into the forbidden zone ... resync the read pointer. - handle->xrun[0] = true; - nextWritePointer = safeWritePointer + handle->dsPointerLeadTime[0] - bufferBytes; - if ( nextWritePointer >= dsBufferSize ) nextWritePointer -= dsBufferSize; - handle->bufferPointer[0] = nextWritePointer; - endWrite = nextWritePointer + bufferBytes; - } - - // Lock free space in the buffer - result = dsBuffer->Lock( nextWritePointer, bufferBytes, &buffer1, - &bufferSize1, &buffer2, &bufferSize2, 0 ); - if ( FAILED( result ) ) { - errorStream_ << "RtApiDs::callbackEvent: error (" << getErrorString( result ) << ") locking buffer during playback!"; - errorText_ = errorStream_.str(); - MUTEX_UNLOCK( &stream_.mutex ); - error( RtAudioError::SYSTEM_ERROR ); - return; - } - - // Copy our buffer into the DS buffer - CopyMemory( buffer1, buffer, bufferSize1 ); - if ( buffer2 != NULL ) CopyMemory( buffer2, buffer+bufferSize1, bufferSize2 ); - - // Update our buffer offset and unlock sound buffer - dsBuffer->Unlock( buffer1, bufferSize1, buffer2, bufferSize2 ); - if ( FAILED( result ) ) { - errorStream_ << "RtApiDs::callbackEvent: error (" << getErrorString( result ) << ") unlocking buffer during playback!"; - errorText_ = errorStream_.str(); - MUTEX_UNLOCK( &stream_.mutex ); - error( RtAudioError::SYSTEM_ERROR ); - return; - } - nextWritePointer = ( nextWritePointer + bufferSize1 + bufferSize2 ) % dsBufferSize; - handle->bufferPointer[0] = nextWritePointer; - } - - // Don't bother draining input - if ( handle->drainCounter ) { - handle->drainCounter++; - goto unlock; - } - - if ( stream_.mode == INPUT || stream_.mode == DUPLEX ) { - - // Setup parameters. - if ( stream_.doConvertBuffer[1] ) { - buffer = stream_.deviceBuffer; - bufferBytes = stream_.bufferSize * stream_.nDeviceChannels[1]; - bufferBytes *= formatBytes( stream_.deviceFormat[1] ); - } - else { - buffer = stream_.userBuffer[1]; - bufferBytes = stream_.bufferSize * stream_.nUserChannels[1]; - bufferBytes *= formatBytes( stream_.userFormat ); - } - - LPDIRECTSOUNDCAPTUREBUFFER dsBuffer = (LPDIRECTSOUNDCAPTUREBUFFER) handle->buffer[1]; - long nextReadPointer = handle->bufferPointer[1]; - DWORD dsBufferSize = handle->dsBufferSize[1]; - - // Find out where the write and "safe read" pointers are. - result = dsBuffer->GetCurrentPosition( ¤tReadPointer, &safeReadPointer ); - if ( FAILED( result ) ) { - errorStream_ << "RtApiDs::callbackEvent: error (" << getErrorString( result ) << ") getting current read position!"; - errorText_ = errorStream_.str(); - MUTEX_UNLOCK( &stream_.mutex ); - error( RtAudioError::SYSTEM_ERROR ); - return; - } - - if ( safeReadPointer < (DWORD)nextReadPointer ) safeReadPointer += dsBufferSize; // unwrap offset - DWORD endRead = nextReadPointer + bufferBytes; - - // Handling depends on whether we are INPUT or DUPLEX. - // If we're in INPUT mode then waiting is a good thing. If we're in DUPLEX mode, - // then a wait here will drag the write pointers into the forbidden zone. - // - // In DUPLEX mode, rather than wait, we will back off the read pointer until - // it's in a safe position. This causes dropouts, but it seems to be the only - // practical way to sync up the read and write pointers reliably, given the - // the very complex relationship between phase and increment of the read and write - // pointers. - // - // In order to minimize audible dropouts in DUPLEX mode, we will - // provide a pre-roll period of 0.5 seconds in which we return - // zeros from the read buffer while the pointers sync up. - - if ( stream_.mode == DUPLEX ) { - if ( safeReadPointer < endRead ) { - if ( duplexPrerollBytes <= 0 ) { - // Pre-roll time over. Be more agressive. - int adjustment = endRead-safeReadPointer; - - handle->xrun[1] = true; - // Two cases: - // - large adjustments: we've probably run out of CPU cycles, so just resync exactly, - // and perform fine adjustments later. - // - small adjustments: back off by twice as much. - if ( adjustment >= 2*bufferBytes ) - nextReadPointer = safeReadPointer-2*bufferBytes; - else - nextReadPointer = safeReadPointer-bufferBytes-adjustment; - - if ( nextReadPointer < 0 ) nextReadPointer += dsBufferSize; - - } - else { - // In pre=roll time. Just do it. - nextReadPointer = safeReadPointer - bufferBytes; - while ( nextReadPointer < 0 ) nextReadPointer += dsBufferSize; - } - endRead = nextReadPointer + bufferBytes; - } - } - else { // mode == INPUT - while ( safeReadPointer < endRead && stream_.callbackInfo.isRunning ) { - // See comments for playback. - double millis = (endRead - safeReadPointer) * 1000.0; - millis /= ( formatBytes(stream_.deviceFormat[1]) * stream_.nDeviceChannels[1] * stream_.sampleRate); - if ( millis < 1.0 ) millis = 1.0; - Sleep( (DWORD) millis ); - - // Wake up and find out where we are now. - result = dsBuffer->GetCurrentPosition( ¤tReadPointer, &safeReadPointer ); - if ( FAILED( result ) ) { - errorStream_ << "RtApiDs::callbackEvent: error (" << getErrorString( result ) << ") getting current read position!"; - errorText_ = errorStream_.str(); - MUTEX_UNLOCK( &stream_.mutex ); - error( RtAudioError::SYSTEM_ERROR ); - return; - } - - if ( safeReadPointer < (DWORD)nextReadPointer ) safeReadPointer += dsBufferSize; // unwrap offset - } - } - - // Lock free space in the buffer - result = dsBuffer->Lock( nextReadPointer, bufferBytes, &buffer1, - &bufferSize1, &buffer2, &bufferSize2, 0 ); - if ( FAILED( result ) ) { - errorStream_ << "RtApiDs::callbackEvent: error (" << getErrorString( result ) << ") locking capture buffer!"; - errorText_ = errorStream_.str(); - MUTEX_UNLOCK( &stream_.mutex ); - error( RtAudioError::SYSTEM_ERROR ); - return; - } - - if ( duplexPrerollBytes <= 0 ) { - // Copy our buffer into the DS buffer - CopyMemory( buffer, buffer1, bufferSize1 ); - if ( buffer2 != NULL ) CopyMemory( buffer+bufferSize1, buffer2, bufferSize2 ); - } - else { - memset( buffer, 0, bufferSize1 ); - if ( buffer2 != NULL ) memset( buffer + bufferSize1, 0, bufferSize2 ); - duplexPrerollBytes -= bufferSize1 + bufferSize2; - } - - // Update our buffer offset and unlock sound buffer - nextReadPointer = ( nextReadPointer + bufferSize1 + bufferSize2 ) % dsBufferSize; - dsBuffer->Unlock( buffer1, bufferSize1, buffer2, bufferSize2 ); - if ( FAILED( result ) ) { - errorStream_ << "RtApiDs::callbackEvent: error (" << getErrorString( result ) << ") unlocking capture buffer!"; - errorText_ = errorStream_.str(); - MUTEX_UNLOCK( &stream_.mutex ); - error( RtAudioError::SYSTEM_ERROR ); - return; - } - handle->bufferPointer[1] = nextReadPointer; - - // No byte swapping necessary in DirectSound implementation. - - // If necessary, convert 8-bit data from unsigned to signed. - if ( stream_.deviceFormat[1] == RTAUDIO_SINT8 ) - for ( int j=0; jobject; - bool* isRunning = &info->isRunning; - - while ( *isRunning == true ) { - object->callbackEvent(); - } - - _endthreadex( 0 ); - return 0; -} - -static BOOL CALLBACK deviceQueryCallback( LPGUID lpguid, - LPCTSTR description, - LPCTSTR /*module*/, - LPVOID lpContext ) -{ - struct DsProbeData& probeInfo = *(struct DsProbeData*) lpContext; - std::vector& dsDevices = *probeInfo.dsDevices; - - HRESULT hr; - bool validDevice = false; - if ( probeInfo.isInput == true ) { - DSCCAPS caps; - LPDIRECTSOUNDCAPTURE object; - - hr = DirectSoundCaptureCreate( lpguid, &object, NULL ); - if ( hr != DS_OK ) return TRUE; - - caps.dwSize = sizeof(caps); - hr = object->GetCaps( &caps ); - if ( hr == DS_OK ) { - if ( caps.dwChannels > 0 && caps.dwFormats > 0 ) - validDevice = true; - } - object->Release(); - } - else { - DSCAPS caps; - LPDIRECTSOUND object; - hr = DirectSoundCreate( lpguid, &object, NULL ); - if ( hr != DS_OK ) return TRUE; - - caps.dwSize = sizeof(caps); - hr = object->GetCaps( &caps ); - if ( hr == DS_OK ) { - if ( caps.dwFlags & DSCAPS_PRIMARYMONO || caps.dwFlags & DSCAPS_PRIMARYSTEREO ) - validDevice = true; - } - object->Release(); - } - - // If good device, then save its name and guid. - std::string name = convertCharPointerToStdString( description ); - //if ( name == "Primary Sound Driver" || name == "Primary Sound Capture Driver" ) - if ( lpguid == NULL ) - name = "Default Device"; - if ( validDevice ) { - for ( unsigned int i=0; i -#include - - // A structure to hold various information related to the ALSA API - // implementation. -struct AlsaHandle { - snd_pcm_t *handles[2]; - bool synchronized; - bool xrun[2]; - pthread_cond_t runnable_cv; - bool runnable; - - AlsaHandle() - :synchronized(false), runnable(false) { xrun[0] = false; xrun[1] = false; } -}; - -static void *alsaCallbackHandler( void * ptr ); - -RtApiAlsa :: RtApiAlsa() -{ - // Nothing to do here. -} - -RtApiAlsa :: ~RtApiAlsa() -{ - if ( stream_.state != STREAM_CLOSED ) closeStream(); -} - -unsigned int RtApiAlsa :: getDeviceCount( void ) -{ - unsigned nDevices = 0; - int result, subdevice, card; - char name[64]; - snd_ctl_t *handle = 0; - - // Count cards and devices - card = -1; - snd_card_next( &card ); - while ( card >= 0 ) { - sprintf( name, "hw:%d", card ); - result = snd_ctl_open( &handle, name, 0 ); - if ( result < 0 ) { - handle = 0; - errorStream_ << "RtApiAlsa::getDeviceCount: control open, card = " << card << ", " << snd_strerror( result ) << "."; - errorText_ = errorStream_.str(); - error( RtAudioError::WARNING ); - goto nextcard; - } - subdevice = -1; - while( 1 ) { - result = snd_ctl_pcm_next_device( handle, &subdevice ); - if ( result < 0 ) { - errorStream_ << "RtApiAlsa::getDeviceCount: control next device, card = " << card << ", " << snd_strerror( result ) << "."; - errorText_ = errorStream_.str(); - error( RtAudioError::WARNING ); - break; - } - if ( subdevice < 0 ) - break; - nDevices++; - } - nextcard: - if ( handle ) - snd_ctl_close( handle ); - snd_card_next( &card ); - } - - result = snd_ctl_open( &handle, "default", 0 ); - if (result == 0) { - nDevices++; - snd_ctl_close( handle ); - } - - return nDevices; -} - -RtAudio::DeviceInfo RtApiAlsa :: getDeviceInfo( unsigned int device ) -{ - RtAudio::DeviceInfo info; - info.probed = false; - - unsigned nDevices = 0; - int result, subdevice, card; - char name[64]; - snd_ctl_t *chandle = 0; - - // Count cards and devices - card = -1; - subdevice = -1; - snd_card_next( &card ); - while ( card >= 0 ) { - sprintf( name, "hw:%d", card ); - result = snd_ctl_open( &chandle, name, SND_CTL_NONBLOCK ); - if ( result < 0 ) { - chandle = 0; - errorStream_ << "RtApiAlsa::getDeviceInfo: control open, card = " << card << ", " << snd_strerror( result ) << "."; - errorText_ = errorStream_.str(); - error( RtAudioError::WARNING ); - goto nextcard; - } - subdevice = -1; - while( 1 ) { - result = snd_ctl_pcm_next_device( chandle, &subdevice ); - if ( result < 0 ) { - errorStream_ << "RtApiAlsa::getDeviceInfo: control next device, card = " << card << ", " << snd_strerror( result ) << "."; - errorText_ = errorStream_.str(); - error( RtAudioError::WARNING ); - break; - } - if ( subdevice < 0 ) break; - if ( nDevices == device ) { - sprintf( name, "hw:%d,%d", card, subdevice ); - goto foundDevice; - } - nDevices++; - } - nextcard: - if ( chandle ) - snd_ctl_close( chandle ); - snd_card_next( &card ); - } - - result = snd_ctl_open( &chandle, "default", SND_CTL_NONBLOCK ); - if ( result == 0 ) { - if ( nDevices == device ) { - strcpy( name, "default" ); - goto foundDevice; - } - nDevices++; - } - - if ( nDevices == 0 ) { - errorText_ = "RtApiAlsa::getDeviceInfo: no devices found!"; - error( RtAudioError::INVALID_USE ); - return info; - } - - if ( device >= nDevices ) { - errorText_ = "RtApiAlsa::getDeviceInfo: device ID is invalid!"; - error( RtAudioError::INVALID_USE ); - return info; - } - - foundDevice: - - // If a stream is already open, we cannot probe the stream devices. - // Thus, use the saved results. - if ( stream_.state != STREAM_CLOSED && - ( stream_.device[0] == device || stream_.device[1] == device ) ) { - snd_ctl_close( chandle ); - if ( device >= devices_.size() ) { - errorText_ = "RtApiAlsa::getDeviceInfo: device ID was not present before stream was opened."; - error( RtAudioError::WARNING ); - return info; - } - return devices_[ device ]; - } - - int openMode = SND_PCM_ASYNC; - snd_pcm_stream_t stream; - snd_pcm_info_t *pcminfo; - snd_pcm_info_alloca( &pcminfo ); - snd_pcm_t *phandle; - snd_pcm_hw_params_t *params; - snd_pcm_hw_params_alloca( ¶ms ); - - // First try for playback unless default device (which has subdev -1) - stream = SND_PCM_STREAM_PLAYBACK; - snd_pcm_info_set_stream( pcminfo, stream ); - if ( subdevice != -1 ) { - snd_pcm_info_set_device( pcminfo, subdevice ); - snd_pcm_info_set_subdevice( pcminfo, 0 ); - - result = snd_ctl_pcm_info( chandle, pcminfo ); - if ( result < 0 ) { - // Device probably doesn't support playback. - goto captureProbe; - } - } - - result = snd_pcm_open( &phandle, name, stream, openMode | SND_PCM_NONBLOCK ); - if ( result < 0 ) { - errorStream_ << "RtApiAlsa::getDeviceInfo: snd_pcm_open error for device (" << name << "), " << snd_strerror( result ) << "."; - errorText_ = errorStream_.str(); - error( RtAudioError::WARNING ); - goto captureProbe; - } - - // The device is open ... fill the parameter structure. - result = snd_pcm_hw_params_any( phandle, params ); - if ( result < 0 ) { - snd_pcm_close( phandle ); - errorStream_ << "RtApiAlsa::getDeviceInfo: snd_pcm_hw_params error for device (" << name << "), " << snd_strerror( result ) << "."; - errorText_ = errorStream_.str(); - error( RtAudioError::WARNING ); - goto captureProbe; - } - - // Get output channel information. - unsigned int value; - result = snd_pcm_hw_params_get_channels_max( params, &value ); - if ( result < 0 ) { - snd_pcm_close( phandle ); - errorStream_ << "RtApiAlsa::getDeviceInfo: error getting device (" << name << ") output channels, " << snd_strerror( result ) << "."; - errorText_ = errorStream_.str(); - error( RtAudioError::WARNING ); - goto captureProbe; - } - info.outputChannels = value; - snd_pcm_close( phandle ); - - captureProbe: - stream = SND_PCM_STREAM_CAPTURE; - snd_pcm_info_set_stream( pcminfo, stream ); - - // Now try for capture unless default device (with subdev = -1) - if ( subdevice != -1 ) { - result = snd_ctl_pcm_info( chandle, pcminfo ); - snd_ctl_close( chandle ); - if ( result < 0 ) { - // Device probably doesn't support capture. - if ( info.outputChannels == 0 ) return info; - goto probeParameters; - } - } - else - snd_ctl_close( chandle ); - - result = snd_pcm_open( &phandle, name, stream, openMode | SND_PCM_NONBLOCK); - if ( result < 0 ) { - errorStream_ << "RtApiAlsa::getDeviceInfo: snd_pcm_open error for device (" << name << "), " << snd_strerror( result ) << "."; - errorText_ = errorStream_.str(); - error( RtAudioError::WARNING ); - if ( info.outputChannels == 0 ) return info; - goto probeParameters; - } - - // The device is open ... fill the parameter structure. - result = snd_pcm_hw_params_any( phandle, params ); - if ( result < 0 ) { - snd_pcm_close( phandle ); - errorStream_ << "RtApiAlsa::getDeviceInfo: snd_pcm_hw_params error for device (" << name << "), " << snd_strerror( result ) << "."; - errorText_ = errorStream_.str(); - error( RtAudioError::WARNING ); - if ( info.outputChannels == 0 ) return info; - goto probeParameters; - } - - result = snd_pcm_hw_params_get_channels_max( params, &value ); - if ( result < 0 ) { - snd_pcm_close( phandle ); - errorStream_ << "RtApiAlsa::getDeviceInfo: error getting device (" << name << ") input channels, " << snd_strerror( result ) << "."; - errorText_ = errorStream_.str(); - error( RtAudioError::WARNING ); - if ( info.outputChannels == 0 ) return info; - goto probeParameters; - } - info.inputChannels = value; - snd_pcm_close( phandle ); - - // If device opens for both playback and capture, we determine the channels. - if ( info.outputChannels > 0 && info.inputChannels > 0 ) - info.duplexChannels = (info.outputChannels > info.inputChannels) ? info.inputChannels : info.outputChannels; - - // ALSA doesn't provide default devices so we'll use the first available one. - if ( device == 0 && info.outputChannels > 0 ) - info.isDefaultOutput = true; - if ( device == 0 && info.inputChannels > 0 ) - info.isDefaultInput = true; - - probeParameters: - // At this point, we just need to figure out the supported data - // formats and sample rates. We'll proceed by opening the device in - // the direction with the maximum number of channels, or playback if - // they are equal. This might limit our sample rate options, but so - // be it. - - if ( info.outputChannels >= info.inputChannels ) - stream = SND_PCM_STREAM_PLAYBACK; - else - stream = SND_PCM_STREAM_CAPTURE; - snd_pcm_info_set_stream( pcminfo, stream ); - - result = snd_pcm_open( &phandle, name, stream, openMode | SND_PCM_NONBLOCK); - if ( result < 0 ) { - errorStream_ << "RtApiAlsa::getDeviceInfo: snd_pcm_open error for device (" << name << "), " << snd_strerror( result ) << "."; - errorText_ = errorStream_.str(); - error( RtAudioError::WARNING ); - return info; - } - - // The device is open ... fill the parameter structure. - result = snd_pcm_hw_params_any( phandle, params ); - if ( result < 0 ) { - snd_pcm_close( phandle ); - errorStream_ << "RtApiAlsa::getDeviceInfo: snd_pcm_hw_params error for device (" << name << "), " << snd_strerror( result ) << "."; - errorText_ = errorStream_.str(); - error( RtAudioError::WARNING ); - return info; - } - - // Test our discrete set of sample rate values. - info.sampleRates.clear(); - for ( unsigned int i=0; i info.preferredSampleRate ) ) - info.preferredSampleRate = SAMPLE_RATES[i]; - } - } - if ( info.sampleRates.size() == 0 ) { - snd_pcm_close( phandle ); - errorStream_ << "RtApiAlsa::getDeviceInfo: no supported sample rates found for device (" << name << ")."; - errorText_ = errorStream_.str(); - error( RtAudioError::WARNING ); - return info; - } - - // Probe the supported data formats ... we don't care about endian-ness just yet - snd_pcm_format_t format; - info.nativeFormats = 0; - format = SND_PCM_FORMAT_S8; - if ( snd_pcm_hw_params_test_format( phandle, params, format ) == 0 ) - info.nativeFormats |= RTAUDIO_SINT8; - format = SND_PCM_FORMAT_S16; - if ( snd_pcm_hw_params_test_format( phandle, params, format ) == 0 ) - info.nativeFormats |= RTAUDIO_SINT16; - format = SND_PCM_FORMAT_S24; - if ( snd_pcm_hw_params_test_format( phandle, params, format ) == 0 ) - info.nativeFormats |= RTAUDIO_SINT24; - format = SND_PCM_FORMAT_S32; - if ( snd_pcm_hw_params_test_format( phandle, params, format ) == 0 ) - info.nativeFormats |= RTAUDIO_SINT32; - format = SND_PCM_FORMAT_FLOAT; - if ( snd_pcm_hw_params_test_format( phandle, params, format ) == 0 ) - info.nativeFormats |= RTAUDIO_FLOAT32; - format = SND_PCM_FORMAT_FLOAT64; - if ( snd_pcm_hw_params_test_format( phandle, params, format ) == 0 ) - info.nativeFormats |= RTAUDIO_FLOAT64; - - // Check that we have at least one supported format - if ( info.nativeFormats == 0 ) { - snd_pcm_close( phandle ); - errorStream_ << "RtApiAlsa::getDeviceInfo: pcm device (" << name << ") data format not supported by RtAudio."; - errorText_ = errorStream_.str(); - error( RtAudioError::WARNING ); - return info; - } - - // Get the device name - char *cardname; - result = snd_card_get_name( card, &cardname ); - if ( result >= 0 ) { - sprintf( name, "hw:%s,%d", cardname, subdevice ); - free( cardname ); - } - info.name = name; - - // That's all ... close the device and return - snd_pcm_close( phandle ); - info.probed = true; - return info; -} - -void RtApiAlsa :: saveDeviceInfo( void ) -{ - devices_.clear(); - - unsigned int nDevices = getDeviceCount(); - devices_.resize( nDevices ); - for ( unsigned int i=0; iflags & RTAUDIO_ALSA_USE_DEFAULT ) - snprintf(name, sizeof(name), "%s", "default"); - else { - // Count cards and devices - card = -1; - snd_card_next( &card ); - while ( card >= 0 ) { - sprintf( name, "hw:%d", card ); - result = snd_ctl_open( &chandle, name, SND_CTL_NONBLOCK ); - if ( result < 0 ) { - errorStream_ << "RtApiAlsa::probeDeviceOpen: control open, card = " << card << ", " << snd_strerror( result ) << "."; - errorText_ = errorStream_.str(); - return FAILURE; - } - subdevice = -1; - while( 1 ) { - result = snd_ctl_pcm_next_device( chandle, &subdevice ); - if ( result < 0 ) break; - if ( subdevice < 0 ) break; - if ( nDevices == device ) { - sprintf( name, "hw:%d,%d", card, subdevice ); - snd_ctl_close( chandle ); - goto foundDevice; - } - nDevices++; - } - snd_ctl_close( chandle ); - snd_card_next( &card ); - } - - result = snd_ctl_open( &chandle, "default", SND_CTL_NONBLOCK ); - if ( result == 0 ) { - if ( nDevices == device ) { - strcpy( name, "default" ); - snd_ctl_close( chandle ); - goto foundDevice; - } - nDevices++; - } - snd_ctl_close( chandle ); - - if ( nDevices == 0 ) { - // This should not happen because a check is made before this function is called. - errorText_ = "RtApiAlsa::probeDeviceOpen: no devices found!"; - return FAILURE; - } - - if ( device >= nDevices ) { - // This should not happen because a check is made before this function is called. - errorText_ = "RtApiAlsa::probeDeviceOpen: device ID is invalid!"; - return FAILURE; - } - } - - foundDevice: - - // The getDeviceInfo() function will not work for a device that is - // already open. Thus, we'll probe the system before opening a - // stream and save the results for use by getDeviceInfo(). - if ( mode == OUTPUT || ( mode == INPUT && stream_.mode != OUTPUT ) ) // only do once - this->saveDeviceInfo(); - - snd_pcm_stream_t stream; - if ( mode == OUTPUT ) - stream = SND_PCM_STREAM_PLAYBACK; - else - stream = SND_PCM_STREAM_CAPTURE; - - snd_pcm_t *phandle; - int openMode = SND_PCM_ASYNC; - result = snd_pcm_open( &phandle, name, stream, openMode ); - if ( result < 0 ) { - if ( mode == OUTPUT ) - errorStream_ << "RtApiAlsa::probeDeviceOpen: pcm device (" << name << ") won't open for output."; - else - errorStream_ << "RtApiAlsa::probeDeviceOpen: pcm device (" << name << ") won't open for input."; - errorText_ = errorStream_.str(); - return FAILURE; - } - - // Fill the parameter structure. - snd_pcm_hw_params_t *hw_params; - snd_pcm_hw_params_alloca( &hw_params ); - result = snd_pcm_hw_params_any( phandle, hw_params ); - if ( result < 0 ) { - snd_pcm_close( phandle ); - errorStream_ << "RtApiAlsa::probeDeviceOpen: error getting pcm device (" << name << ") parameters, " << snd_strerror( result ) << "."; - errorText_ = errorStream_.str(); - return FAILURE; - } - -#if defined(__RTAUDIO_DEBUG__) - fprintf( stderr, "\nRtApiAlsa: dump hardware params just after device open:\n\n" ); - snd_pcm_hw_params_dump( hw_params, out ); -#endif - - // Set access ... check user preference. - if ( options && options->flags & RTAUDIO_NONINTERLEAVED ) { - stream_.userInterleaved = false; - result = snd_pcm_hw_params_set_access( phandle, hw_params, SND_PCM_ACCESS_RW_NONINTERLEAVED ); - if ( result < 0 ) { - result = snd_pcm_hw_params_set_access( phandle, hw_params, SND_PCM_ACCESS_RW_INTERLEAVED ); - stream_.deviceInterleaved[mode] = true; - } - else - stream_.deviceInterleaved[mode] = false; - } - else { - stream_.userInterleaved = true; - result = snd_pcm_hw_params_set_access( phandle, hw_params, SND_PCM_ACCESS_RW_INTERLEAVED ); - if ( result < 0 ) { - result = snd_pcm_hw_params_set_access( phandle, hw_params, SND_PCM_ACCESS_RW_NONINTERLEAVED ); - stream_.deviceInterleaved[mode] = false; - } - else - stream_.deviceInterleaved[mode] = true; - } - - if ( result < 0 ) { - snd_pcm_close( phandle ); - errorStream_ << "RtApiAlsa::probeDeviceOpen: error setting pcm device (" << name << ") access, " << snd_strerror( result ) << "."; - errorText_ = errorStream_.str(); - return FAILURE; - } - - // Determine how to set the device format. - stream_.userFormat = format; - snd_pcm_format_t deviceFormat = SND_PCM_FORMAT_UNKNOWN; - - if ( format == RTAUDIO_SINT8 ) - deviceFormat = SND_PCM_FORMAT_S8; - else if ( format == RTAUDIO_SINT16 ) - deviceFormat = SND_PCM_FORMAT_S16; - else if ( format == RTAUDIO_SINT24 ) - deviceFormat = SND_PCM_FORMAT_S24; - else if ( format == RTAUDIO_SINT32 ) - deviceFormat = SND_PCM_FORMAT_S32; - else if ( format == RTAUDIO_FLOAT32 ) - deviceFormat = SND_PCM_FORMAT_FLOAT; - else if ( format == RTAUDIO_FLOAT64 ) - deviceFormat = SND_PCM_FORMAT_FLOAT64; - - if ( snd_pcm_hw_params_test_format(phandle, hw_params, deviceFormat) == 0) { - stream_.deviceFormat[mode] = format; - goto setFormat; - } - - // The user requested format is not natively supported by the device. - deviceFormat = SND_PCM_FORMAT_FLOAT64; - if ( snd_pcm_hw_params_test_format( phandle, hw_params, deviceFormat ) == 0 ) { - stream_.deviceFormat[mode] = RTAUDIO_FLOAT64; - goto setFormat; - } - - deviceFormat = SND_PCM_FORMAT_FLOAT; - if ( snd_pcm_hw_params_test_format(phandle, hw_params, deviceFormat ) == 0 ) { - stream_.deviceFormat[mode] = RTAUDIO_FLOAT32; - goto setFormat; - } - - deviceFormat = SND_PCM_FORMAT_S32; - if ( snd_pcm_hw_params_test_format(phandle, hw_params, deviceFormat ) == 0 ) { - stream_.deviceFormat[mode] = RTAUDIO_SINT32; - goto setFormat; - } - - deviceFormat = SND_PCM_FORMAT_S24; - if ( snd_pcm_hw_params_test_format(phandle, hw_params, deviceFormat ) == 0 ) { - stream_.deviceFormat[mode] = RTAUDIO_SINT24; - goto setFormat; - } - - deviceFormat = SND_PCM_FORMAT_S16; - if ( snd_pcm_hw_params_test_format(phandle, hw_params, deviceFormat ) == 0 ) { - stream_.deviceFormat[mode] = RTAUDIO_SINT16; - goto setFormat; - } - - deviceFormat = SND_PCM_FORMAT_S8; - if ( snd_pcm_hw_params_test_format(phandle, hw_params, deviceFormat ) == 0 ) { - stream_.deviceFormat[mode] = RTAUDIO_SINT8; - goto setFormat; - } - - // If we get here, no supported format was found. - snd_pcm_close( phandle ); - errorStream_ << "RtApiAlsa::probeDeviceOpen: pcm device " << device << " data format not supported by RtAudio."; - errorText_ = errorStream_.str(); - return FAILURE; - - setFormat: - result = snd_pcm_hw_params_set_format( phandle, hw_params, deviceFormat ); - if ( result < 0 ) { - snd_pcm_close( phandle ); - errorStream_ << "RtApiAlsa::probeDeviceOpen: error setting pcm device (" << name << ") data format, " << snd_strerror( result ) << "."; - errorText_ = errorStream_.str(); - return FAILURE; - } - - // Determine whether byte-swaping is necessary. - stream_.doByteSwap[mode] = false; - if ( deviceFormat != SND_PCM_FORMAT_S8 ) { - result = snd_pcm_format_cpu_endian( deviceFormat ); - if ( result == 0 ) - stream_.doByteSwap[mode] = true; - else if (result < 0) { - snd_pcm_close( phandle ); - errorStream_ << "RtApiAlsa::probeDeviceOpen: error getting pcm device (" << name << ") endian-ness, " << snd_strerror( result ) << "."; - errorText_ = errorStream_.str(); - return FAILURE; - } - } - - // Set the sample rate. - result = snd_pcm_hw_params_set_rate_near( phandle, hw_params, (unsigned int*) &sampleRate, 0 ); - if ( result < 0 ) { - snd_pcm_close( phandle ); - errorStream_ << "RtApiAlsa::probeDeviceOpen: error setting sample rate on device (" << name << "), " << snd_strerror( result ) << "."; - errorText_ = errorStream_.str(); - return FAILURE; - } - - // Determine the number of channels for this device. We support a possible - // minimum device channel number > than the value requested by the user. - stream_.nUserChannels[mode] = channels; - unsigned int value; - result = snd_pcm_hw_params_get_channels_max( hw_params, &value ); - unsigned int deviceChannels = value; - if ( result < 0 || deviceChannels < channels + firstChannel ) { - snd_pcm_close( phandle ); - errorStream_ << "RtApiAlsa::probeDeviceOpen: requested channel parameters not supported by device (" << name << "), " << snd_strerror( result ) << "."; - errorText_ = errorStream_.str(); - return FAILURE; - } - - result = snd_pcm_hw_params_get_channels_min( hw_params, &value ); - if ( result < 0 ) { - snd_pcm_close( phandle ); - errorStream_ << "RtApiAlsa::probeDeviceOpen: error getting minimum channels for device (" << name << "), " << snd_strerror( result ) << "."; - errorText_ = errorStream_.str(); - return FAILURE; - } - deviceChannels = value; - if ( deviceChannels < channels + firstChannel ) deviceChannels = channels + firstChannel; - stream_.nDeviceChannels[mode] = deviceChannels; - - // Set the device channels. - result = snd_pcm_hw_params_set_channels( phandle, hw_params, deviceChannels ); - if ( result < 0 ) { - snd_pcm_close( phandle ); - errorStream_ << "RtApiAlsa::probeDeviceOpen: error setting channels for device (" << name << "), " << snd_strerror( result ) << "."; - errorText_ = errorStream_.str(); - return FAILURE; - } - - // Set the buffer (or period) size. - int dir = 0; - snd_pcm_uframes_t periodSize = *bufferSize; - result = snd_pcm_hw_params_set_period_size_near( phandle, hw_params, &periodSize, &dir ); - if ( result < 0 ) { - snd_pcm_close( phandle ); - errorStream_ << "RtApiAlsa::probeDeviceOpen: error setting period size for device (" << name << "), " << snd_strerror( result ) << "."; - errorText_ = errorStream_.str(); - return FAILURE; - } - *bufferSize = periodSize; - - // Set the buffer number, which in ALSA is referred to as the "period". - unsigned int periods = 0; - if ( options && options->flags & RTAUDIO_MINIMIZE_LATENCY ) periods = 2; - if ( options && options->numberOfBuffers > 0 ) periods = options->numberOfBuffers; - if ( periods < 2 ) periods = 4; // a fairly safe default value - result = snd_pcm_hw_params_set_periods_near( phandle, hw_params, &periods, &dir ); - if ( result < 0 ) { - snd_pcm_close( phandle ); - errorStream_ << "RtApiAlsa::probeDeviceOpen: error setting periods for device (" << name << "), " << snd_strerror( result ) << "."; - errorText_ = errorStream_.str(); - return FAILURE; - } - - // If attempting to setup a duplex stream, the bufferSize parameter - // MUST be the same in both directions! - if ( stream_.mode == OUTPUT && mode == INPUT && *bufferSize != stream_.bufferSize ) { - snd_pcm_close( phandle ); - errorStream_ << "RtApiAlsa::probeDeviceOpen: system error setting buffer size for duplex stream on device (" << name << ")."; - errorText_ = errorStream_.str(); - return FAILURE; - } - - stream_.bufferSize = *bufferSize; - - // Install the hardware configuration - result = snd_pcm_hw_params( phandle, hw_params ); - if ( result < 0 ) { - snd_pcm_close( phandle ); - errorStream_ << "RtApiAlsa::probeDeviceOpen: error installing hardware configuration on device (" << name << "), " << snd_strerror( result ) << "."; - errorText_ = errorStream_.str(); - return FAILURE; - } - -#if defined(__RTAUDIO_DEBUG__) - fprintf(stderr, "\nRtApiAlsa: dump hardware params after installation:\n\n"); - snd_pcm_hw_params_dump( hw_params, out ); -#endif - - // Set the software configuration to fill buffers with zeros and prevent device stopping on xruns. - snd_pcm_sw_params_t *sw_params = NULL; - snd_pcm_sw_params_alloca( &sw_params ); - snd_pcm_sw_params_current( phandle, sw_params ); - snd_pcm_sw_params_set_start_threshold( phandle, sw_params, *bufferSize ); - snd_pcm_sw_params_set_stop_threshold( phandle, sw_params, ULONG_MAX ); - snd_pcm_sw_params_set_silence_threshold( phandle, sw_params, 0 ); - - // The following two settings were suggested by Theo Veenker - //snd_pcm_sw_params_set_avail_min( phandle, sw_params, *bufferSize ); - //snd_pcm_sw_params_set_xfer_align( phandle, sw_params, 1 ); - - // here are two options for a fix - //snd_pcm_sw_params_set_silence_size( phandle, sw_params, ULONG_MAX ); - snd_pcm_uframes_t val; - snd_pcm_sw_params_get_boundary( sw_params, &val ); - snd_pcm_sw_params_set_silence_size( phandle, sw_params, val ); - - result = snd_pcm_sw_params( phandle, sw_params ); - if ( result < 0 ) { - snd_pcm_close( phandle ); - errorStream_ << "RtApiAlsa::probeDeviceOpen: error installing software configuration on device (" << name << "), " << snd_strerror( result ) << "."; - errorText_ = errorStream_.str(); - return FAILURE; - } - -#if defined(__RTAUDIO_DEBUG__) - fprintf(stderr, "\nRtApiAlsa: dump software params after installation:\n\n"); - snd_pcm_sw_params_dump( sw_params, out ); -#endif - - // Set flags for buffer conversion - stream_.doConvertBuffer[mode] = false; - if ( stream_.userFormat != stream_.deviceFormat[mode] ) - stream_.doConvertBuffer[mode] = true; - if ( stream_.nUserChannels[mode] < stream_.nDeviceChannels[mode] ) - stream_.doConvertBuffer[mode] = true; - if ( stream_.userInterleaved != stream_.deviceInterleaved[mode] && - stream_.nUserChannels[mode] > 1 ) - stream_.doConvertBuffer[mode] = true; - - // Allocate the ApiHandle if necessary and then save. - AlsaHandle *apiInfo = 0; - if ( stream_.apiHandle == 0 ) { - try { - apiInfo = (AlsaHandle *) new AlsaHandle; - } - catch ( std::bad_alloc& ) { - errorText_ = "RtApiAlsa::probeDeviceOpen: error allocating AlsaHandle memory."; - goto error; - } - - if ( pthread_cond_init( &apiInfo->runnable_cv, NULL ) ) { - errorText_ = "RtApiAlsa::probeDeviceOpen: error initializing pthread condition variable."; - goto error; - } - - stream_.apiHandle = (void *) apiInfo; - apiInfo->handles[0] = 0; - apiInfo->handles[1] = 0; - } - else { - apiInfo = (AlsaHandle *) stream_.apiHandle; - } - apiInfo->handles[mode] = phandle; - phandle = 0; - - // Allocate necessary internal buffers. - unsigned long bufferBytes; - bufferBytes = stream_.nUserChannels[mode] * *bufferSize * formatBytes( stream_.userFormat ); - stream_.userBuffer[mode] = (char *) calloc( bufferBytes, 1 ); - if ( stream_.userBuffer[mode] == NULL ) { - errorText_ = "RtApiAlsa::probeDeviceOpen: error allocating user buffer memory."; - goto error; - } - - if ( stream_.doConvertBuffer[mode] ) { - - bool makeBuffer = true; - bufferBytes = stream_.nDeviceChannels[mode] * formatBytes( stream_.deviceFormat[mode] ); - if ( mode == INPUT ) { - if ( stream_.mode == OUTPUT && stream_.deviceBuffer ) { - unsigned long bytesOut = stream_.nDeviceChannels[0] * formatBytes( stream_.deviceFormat[0] ); - if ( bufferBytes <= bytesOut ) makeBuffer = false; - } - } - - if ( makeBuffer ) { - bufferBytes *= *bufferSize; - if ( stream_.deviceBuffer ) free( stream_.deviceBuffer ); - stream_.deviceBuffer = (char *) calloc( bufferBytes, 1 ); - if ( stream_.deviceBuffer == NULL ) { - errorText_ = "RtApiAlsa::probeDeviceOpen: error allocating device buffer memory."; - goto error; - } - } - } - - stream_.sampleRate = sampleRate; - stream_.nBuffers = periods; - stream_.device[mode] = device; - stream_.state = STREAM_STOPPED; - - // Setup the buffer conversion information structure. - if ( stream_.doConvertBuffer[mode] ) setConvertInfo( mode, firstChannel ); - - // Setup thread if necessary. - if ( stream_.mode == OUTPUT && mode == INPUT ) { - // We had already set up an output stream. - stream_.mode = DUPLEX; - // Link the streams if possible. - apiInfo->synchronized = false; - if ( snd_pcm_link( apiInfo->handles[0], apiInfo->handles[1] ) == 0 ) - apiInfo->synchronized = true; - else { - errorText_ = "RtApiAlsa::probeDeviceOpen: unable to synchronize input and output devices."; - error( RtAudioError::WARNING ); - } - } - else { - stream_.mode = mode; - - // Setup callback thread. - stream_.callbackInfo.object = (void *) this; - - // Set the thread attributes for joinable and realtime scheduling - // priority (optional). The higher priority will only take affect - // if the program is run as root or suid. Note, under Linux - // processes with CAP_SYS_NICE privilege, a user can change - // scheduling policy and priority (thus need not be root). See - // POSIX "capabilities". - pthread_attr_t attr; - pthread_attr_init( &attr ); - pthread_attr_setdetachstate( &attr, PTHREAD_CREATE_JOINABLE ); -#ifdef SCHED_RR // Undefined with some OSes (e.g. NetBSD 1.6.x with GNU Pthread) - if ( options && options->flags & RTAUDIO_SCHEDULE_REALTIME ) { - stream_.callbackInfo.doRealtime = true; - struct sched_param param; - int priority = options->priority; - int min = sched_get_priority_min( SCHED_RR ); - int max = sched_get_priority_max( SCHED_RR ); - if ( priority < min ) priority = min; - else if ( priority > max ) priority = max; - param.sched_priority = priority; - - // Set the policy BEFORE the priority. Otherwise it fails. - pthread_attr_setschedpolicy(&attr, SCHED_RR); - pthread_attr_setscope (&attr, PTHREAD_SCOPE_SYSTEM); - // This is definitely required. Otherwise it fails. - pthread_attr_setinheritsched(&attr, PTHREAD_EXPLICIT_SCHED); - pthread_attr_setschedparam(&attr, ¶m); - } - else - pthread_attr_setschedpolicy( &attr, SCHED_OTHER ); -#else - pthread_attr_setschedpolicy( &attr, SCHED_OTHER ); -#endif - - stream_.callbackInfo.isRunning = true; - result = pthread_create( &stream_.callbackInfo.thread, &attr, alsaCallbackHandler, &stream_.callbackInfo ); - pthread_attr_destroy( &attr ); - if ( result ) { - // Failed. Try instead with default attributes. - result = pthread_create( &stream_.callbackInfo.thread, NULL, alsaCallbackHandler, &stream_.callbackInfo ); - if ( result ) { - stream_.callbackInfo.isRunning = false; - errorText_ = "RtApiAlsa::error creating callback thread!"; - goto error; - } - } - } - - return SUCCESS; - - error: - if ( apiInfo ) { - pthread_cond_destroy( &apiInfo->runnable_cv ); - if ( apiInfo->handles[0] ) snd_pcm_close( apiInfo->handles[0] ); - if ( apiInfo->handles[1] ) snd_pcm_close( apiInfo->handles[1] ); - delete apiInfo; - stream_.apiHandle = 0; - } - - if ( phandle) snd_pcm_close( phandle ); - - for ( int i=0; i<2; i++ ) { - if ( stream_.userBuffer[i] ) { - free( stream_.userBuffer[i] ); - stream_.userBuffer[i] = 0; - } - } - - if ( stream_.deviceBuffer ) { - free( stream_.deviceBuffer ); - stream_.deviceBuffer = 0; - } - - stream_.state = STREAM_CLOSED; - return FAILURE; -} - -void RtApiAlsa :: closeStream() -{ - if ( stream_.state == STREAM_CLOSED ) { - errorText_ = "RtApiAlsa::closeStream(): no open stream to close!"; - error( RtAudioError::WARNING ); - return; - } - - AlsaHandle *apiInfo = (AlsaHandle *) stream_.apiHandle; - stream_.callbackInfo.isRunning = false; - MUTEX_LOCK( &stream_.mutex ); - if ( stream_.state == STREAM_STOPPED ) { - apiInfo->runnable = true; - pthread_cond_signal( &apiInfo->runnable_cv ); - } - MUTEX_UNLOCK( &stream_.mutex ); - pthread_join( stream_.callbackInfo.thread, NULL ); - - if ( stream_.state == STREAM_RUNNING ) { - stream_.state = STREAM_STOPPED; - if ( stream_.mode == OUTPUT || stream_.mode == DUPLEX ) - snd_pcm_drop( apiInfo->handles[0] ); - if ( stream_.mode == INPUT || stream_.mode == DUPLEX ) - snd_pcm_drop( apiInfo->handles[1] ); - } - - if ( apiInfo ) { - pthread_cond_destroy( &apiInfo->runnable_cv ); - if ( apiInfo->handles[0] ) snd_pcm_close( apiInfo->handles[0] ); - if ( apiInfo->handles[1] ) snd_pcm_close( apiInfo->handles[1] ); - delete apiInfo; - stream_.apiHandle = 0; - } - - for ( int i=0; i<2; i++ ) { - if ( stream_.userBuffer[i] ) { - free( stream_.userBuffer[i] ); - stream_.userBuffer[i] = 0; - } - } - - if ( stream_.deviceBuffer ) { - free( stream_.deviceBuffer ); - stream_.deviceBuffer = 0; - } - - stream_.mode = UNINITIALIZED; - stream_.state = STREAM_CLOSED; -} - -void RtApiAlsa :: startStream() -{ - // This method calls snd_pcm_prepare if the device isn't already in that state. - - verifyStream(); - if ( stream_.state == STREAM_RUNNING ) { - errorText_ = "RtApiAlsa::startStream(): the stream is already running!"; - error( RtAudioError::WARNING ); - return; - } - - MUTEX_LOCK( &stream_.mutex ); - - #if defined( HAVE_GETTIMEOFDAY ) - gettimeofday( &stream_.lastTickTimestamp, NULL ); - #endif - - int result = 0; - snd_pcm_state_t state; - AlsaHandle *apiInfo = (AlsaHandle *) stream_.apiHandle; - snd_pcm_t **handle = (snd_pcm_t **) apiInfo->handles; - if ( stream_.mode == OUTPUT || stream_.mode == DUPLEX ) { - state = snd_pcm_state( handle[0] ); - if ( state != SND_PCM_STATE_PREPARED ) { - result = snd_pcm_prepare( handle[0] ); - if ( result < 0 ) { - errorStream_ << "RtApiAlsa::startStream: error preparing output pcm device, " << snd_strerror( result ) << "."; - errorText_ = errorStream_.str(); - goto unlock; - } - } - } - - if ( ( stream_.mode == INPUT || stream_.mode == DUPLEX ) && !apiInfo->synchronized ) { - result = snd_pcm_drop(handle[1]); // fix to remove stale data received since device has been open - state = snd_pcm_state( handle[1] ); - if ( state != SND_PCM_STATE_PREPARED ) { - result = snd_pcm_prepare( handle[1] ); - if ( result < 0 ) { - errorStream_ << "RtApiAlsa::startStream: error preparing input pcm device, " << snd_strerror( result ) << "."; - errorText_ = errorStream_.str(); - goto unlock; - } - } - } - - stream_.state = STREAM_RUNNING; - - unlock: - apiInfo->runnable = true; - pthread_cond_signal( &apiInfo->runnable_cv ); - MUTEX_UNLOCK( &stream_.mutex ); - - if ( result >= 0 ) return; - error( RtAudioError::SYSTEM_ERROR ); -} - -void RtApiAlsa :: stopStream() -{ - verifyStream(); - if ( stream_.state == STREAM_STOPPED ) { - errorText_ = "RtApiAlsa::stopStream(): the stream is already stopped!"; - error( RtAudioError::WARNING ); - return; - } - - stream_.state = STREAM_STOPPED; - MUTEX_LOCK( &stream_.mutex ); - - int result = 0; - AlsaHandle *apiInfo = (AlsaHandle *) stream_.apiHandle; - snd_pcm_t **handle = (snd_pcm_t **) apiInfo->handles; - if ( stream_.mode == OUTPUT || stream_.mode == DUPLEX ) { - if ( apiInfo->synchronized ) - result = snd_pcm_drop( handle[0] ); - else - result = snd_pcm_drain( handle[0] ); - if ( result < 0 ) { - errorStream_ << "RtApiAlsa::stopStream: error draining output pcm device, " << snd_strerror( result ) << "."; - errorText_ = errorStream_.str(); - goto unlock; - } - } - - if ( ( stream_.mode == INPUT || stream_.mode == DUPLEX ) && !apiInfo->synchronized ) { - result = snd_pcm_drop( handle[1] ); - if ( result < 0 ) { - errorStream_ << "RtApiAlsa::stopStream: error stopping input pcm device, " << snd_strerror( result ) << "."; - errorText_ = errorStream_.str(); - goto unlock; - } - } - - unlock: - apiInfo->runnable = false; // fixes high CPU usage when stopped - MUTEX_UNLOCK( &stream_.mutex ); - - if ( result >= 0 ) return; - error( RtAudioError::SYSTEM_ERROR ); -} - -void RtApiAlsa :: abortStream() -{ - verifyStream(); - if ( stream_.state == STREAM_STOPPED ) { - errorText_ = "RtApiAlsa::abortStream(): the stream is already stopped!"; - error( RtAudioError::WARNING ); - return; - } - - stream_.state = STREAM_STOPPED; - MUTEX_LOCK( &stream_.mutex ); - - int result = 0; - AlsaHandle *apiInfo = (AlsaHandle *) stream_.apiHandle; - snd_pcm_t **handle = (snd_pcm_t **) apiInfo->handles; - if ( stream_.mode == OUTPUT || stream_.mode == DUPLEX ) { - result = snd_pcm_drop( handle[0] ); - if ( result < 0 ) { - errorStream_ << "RtApiAlsa::abortStream: error aborting output pcm device, " << snd_strerror( result ) << "."; - errorText_ = errorStream_.str(); - goto unlock; - } - } - - if ( ( stream_.mode == INPUT || stream_.mode == DUPLEX ) && !apiInfo->synchronized ) { - result = snd_pcm_drop( handle[1] ); - if ( result < 0 ) { - errorStream_ << "RtApiAlsa::abortStream: error aborting input pcm device, " << snd_strerror( result ) << "."; - errorText_ = errorStream_.str(); - goto unlock; - } - } - - unlock: - apiInfo->runnable = false; // fixes high CPU usage when stopped - MUTEX_UNLOCK( &stream_.mutex ); - - if ( result >= 0 ) return; - error( RtAudioError::SYSTEM_ERROR ); -} - -void RtApiAlsa :: callbackEvent() -{ - AlsaHandle *apiInfo = (AlsaHandle *) stream_.apiHandle; - if ( stream_.state == STREAM_STOPPED ) { - MUTEX_LOCK( &stream_.mutex ); - while ( !apiInfo->runnable ) - pthread_cond_wait( &apiInfo->runnable_cv, &stream_.mutex ); - - if ( stream_.state != STREAM_RUNNING ) { - MUTEX_UNLOCK( &stream_.mutex ); - return; - } - MUTEX_UNLOCK( &stream_.mutex ); - } - - if ( stream_.state == STREAM_CLOSED ) { - errorText_ = "RtApiAlsa::callbackEvent(): the stream is closed ... this shouldn't happen!"; - error( RtAudioError::WARNING ); - return; - } - - int doStopStream = 0; - RtAudioCallback callback = (RtAudioCallback) stream_.callbackInfo.callback; - double streamTime = getStreamTime(); - RtAudioStreamStatus status = 0; - if ( stream_.mode != INPUT && apiInfo->xrun[0] == true ) { - status |= RTAUDIO_OUTPUT_UNDERFLOW; - apiInfo->xrun[0] = false; - } - if ( stream_.mode != OUTPUT && apiInfo->xrun[1] == true ) { - status |= RTAUDIO_INPUT_OVERFLOW; - apiInfo->xrun[1] = false; - } - doStopStream = callback( stream_.userBuffer[0], stream_.userBuffer[1], - stream_.bufferSize, streamTime, status, stream_.callbackInfo.userData ); - - if ( doStopStream == 2 ) { - abortStream(); - return; - } - - MUTEX_LOCK( &stream_.mutex ); - - // The state might change while waiting on a mutex. - if ( stream_.state == STREAM_STOPPED ) goto unlock; - - int result; - char *buffer; - int channels; - snd_pcm_t **handle; - snd_pcm_sframes_t frames; - RtAudioFormat format; - handle = (snd_pcm_t **) apiInfo->handles; - - if ( stream_.mode == INPUT || stream_.mode == DUPLEX ) { - - // Setup parameters. - if ( stream_.doConvertBuffer[1] ) { - buffer = stream_.deviceBuffer; - channels = stream_.nDeviceChannels[1]; - format = stream_.deviceFormat[1]; - } - else { - buffer = stream_.userBuffer[1]; - channels = stream_.nUserChannels[1]; - format = stream_.userFormat; - } - - // Read samples from device in interleaved/non-interleaved format. - if ( stream_.deviceInterleaved[1] ) - result = snd_pcm_readi( handle[1], buffer, stream_.bufferSize ); - else { - void *bufs[channels]; - size_t offset = stream_.bufferSize * formatBytes( format ); - for ( int i=0; ixrun[1] = true; - result = snd_pcm_prepare( handle[1] ); - if ( result < 0 ) { - errorStream_ << "RtApiAlsa::callbackEvent: error preparing device after overrun, " << snd_strerror( result ) << "."; - errorText_ = errorStream_.str(); - } - } - else { - errorStream_ << "RtApiAlsa::callbackEvent: error, current state is " << snd_pcm_state_name( state ) << ", " << snd_strerror( result ) << "."; - errorText_ = errorStream_.str(); - } - } - else { - errorStream_ << "RtApiAlsa::callbackEvent: audio read error, " << snd_strerror( result ) << "."; - errorText_ = errorStream_.str(); - } - error( RtAudioError::WARNING ); - goto tryOutput; - } - - // Do byte swapping if necessary. - if ( stream_.doByteSwap[1] ) - byteSwapBuffer( buffer, stream_.bufferSize * channels, format ); - - // Do buffer conversion if necessary. - if ( stream_.doConvertBuffer[1] ) - convertBuffer( stream_.userBuffer[1], stream_.deviceBuffer, stream_.convertInfo[1] ); - - // Check stream latency - result = snd_pcm_delay( handle[1], &frames ); - if ( result == 0 && frames > 0 ) stream_.latency[1] = frames; - } - - tryOutput: - - if ( stream_.mode == OUTPUT || stream_.mode == DUPLEX ) { - - // Setup parameters and do buffer conversion if necessary. - if ( stream_.doConvertBuffer[0] ) { - buffer = stream_.deviceBuffer; - convertBuffer( buffer, stream_.userBuffer[0], stream_.convertInfo[0] ); - channels = stream_.nDeviceChannels[0]; - format = stream_.deviceFormat[0]; - } - else { - buffer = stream_.userBuffer[0]; - channels = stream_.nUserChannels[0]; - format = stream_.userFormat; - } - - // Do byte swapping if necessary. - if ( stream_.doByteSwap[0] ) - byteSwapBuffer(buffer, stream_.bufferSize * channels, format); - - // Write samples to device in interleaved/non-interleaved format. - if ( stream_.deviceInterleaved[0] ) - result = snd_pcm_writei( handle[0], buffer, stream_.bufferSize ); - else { - void *bufs[channels]; - size_t offset = stream_.bufferSize * formatBytes( format ); - for ( int i=0; ixrun[0] = true; - result = snd_pcm_prepare( handle[0] ); - if ( result < 0 ) { - errorStream_ << "RtApiAlsa::callbackEvent: error preparing device after underrun, " << snd_strerror( result ) << "."; - errorText_ = errorStream_.str(); - } - else - errorText_ = "RtApiAlsa::callbackEvent: audio write error, underrun."; - } - else { - errorStream_ << "RtApiAlsa::callbackEvent: error, current state is " << snd_pcm_state_name( state ) << ", " << snd_strerror( result ) << "."; - errorText_ = errorStream_.str(); - } - } - else { - errorStream_ << "RtApiAlsa::callbackEvent: audio write error, " << snd_strerror( result ) << "."; - errorText_ = errorStream_.str(); - } - error( RtAudioError::WARNING ); - goto unlock; - } - - // Check stream latency - result = snd_pcm_delay( handle[0], &frames ); - if ( result == 0 && frames > 0 ) stream_.latency[0] = frames; - } - - unlock: - MUTEX_UNLOCK( &stream_.mutex ); - - RtApi::tickStreamTime(); - if ( doStopStream == 1 ) this->stopStream(); -} - -static void *alsaCallbackHandler( void *ptr ) -{ - CallbackInfo *info = (CallbackInfo *) ptr; - RtApiAlsa *object = (RtApiAlsa *) info->object; - bool *isRunning = &info->isRunning; - -#ifdef SCHED_RR // Undefined with some OSes (e.g. NetBSD 1.6.x with GNU Pthread) - if ( info->doRealtime ) { - std::cerr << "RtAudio alsa: " << - (sched_getscheduler(0) == SCHED_RR ? "" : "_NOT_ ") << - "running realtime scheduling" << std::endl; - } -#endif - - while ( *isRunning == true ) { - pthread_testcancel(); - object->callbackEvent(); - } - - pthread_exit( NULL ); -} - -//******************** End of __LINUX_ALSA__ *********************// -#endif - -#if defined(__LINUX_PULSE__) - -// Code written by Peter Meerwald, pmeerw@pmeerw.net -// and Tristan Matthews. - -#include -#include -#include - -static const unsigned int SUPPORTED_SAMPLERATES[] = { 8000, 16000, 22050, 32000, - 44100, 48000, 96000, 0}; - -struct rtaudio_pa_format_mapping_t { - RtAudioFormat rtaudio_format; - pa_sample_format_t pa_format; -}; - -static const rtaudio_pa_format_mapping_t supported_sampleformats[] = { - {RTAUDIO_SINT16, PA_SAMPLE_S16LE}, - {RTAUDIO_SINT32, PA_SAMPLE_S32LE}, - {RTAUDIO_FLOAT32, PA_SAMPLE_FLOAT32LE}, - {0, PA_SAMPLE_INVALID}}; - -struct PulseAudioHandle { - pa_simple *s_play; - pa_simple *s_rec; - pthread_t thread; - pthread_cond_t runnable_cv; - bool runnable; - PulseAudioHandle() : s_play(0), s_rec(0), runnable(false) { } -}; - -RtApiPulse::~RtApiPulse() -{ - if ( stream_.state != STREAM_CLOSED ) - closeStream(); -} - -unsigned int RtApiPulse::getDeviceCount( void ) -{ - return 1; -} - -RtAudio::DeviceInfo RtApiPulse::getDeviceInfo( unsigned int /*device*/ ) -{ - RtAudio::DeviceInfo info; - info.probed = true; - info.name = "PulseAudio"; - info.outputChannels = 2; - info.inputChannels = 2; - info.duplexChannels = 2; - info.isDefaultOutput = true; - info.isDefaultInput = true; - - for ( const unsigned int *sr = SUPPORTED_SAMPLERATES; *sr; ++sr ) - info.sampleRates.push_back( *sr ); - - info.preferredSampleRate = 48000; - info.nativeFormats = RTAUDIO_SINT16 | RTAUDIO_SINT32 | RTAUDIO_FLOAT32; - - return info; -} - -static void *pulseaudio_callback( void * user ) -{ - CallbackInfo *cbi = static_cast( user ); - RtApiPulse *context = static_cast( cbi->object ); - volatile bool *isRunning = &cbi->isRunning; - -#ifdef SCHED_RR // Undefined with some OSes (e.g. NetBSD 1.6.x with GNU Pthread) - if (cbi->doRealtime) { - std::cerr << "RtAudio pulse: " << - (sched_getscheduler(0) == SCHED_RR ? "" : "_NOT_ ") << - "running realtime scheduling" << std::endl; - } -#endif - - while ( *isRunning ) { - pthread_testcancel(); - context->callbackEvent(); - } - - pthread_exit( NULL ); -} - -void RtApiPulse::closeStream( void ) -{ - PulseAudioHandle *pah = static_cast( stream_.apiHandle ); - - stream_.callbackInfo.isRunning = false; - if ( pah ) { - MUTEX_LOCK( &stream_.mutex ); - if ( stream_.state == STREAM_STOPPED ) { - pah->runnable = true; - pthread_cond_signal( &pah->runnable_cv ); - } - MUTEX_UNLOCK( &stream_.mutex ); - - pthread_join( pah->thread, 0 ); - if ( pah->s_play ) { - pa_simple_flush( pah->s_play, NULL ); - pa_simple_free( pah->s_play ); - } - if ( pah->s_rec ) - pa_simple_free( pah->s_rec ); - - pthread_cond_destroy( &pah->runnable_cv ); - delete pah; - stream_.apiHandle = 0; - } - - if ( stream_.userBuffer[0] ) { - free( stream_.userBuffer[0] ); - stream_.userBuffer[0] = 0; - } - if ( stream_.userBuffer[1] ) { - free( stream_.userBuffer[1] ); - stream_.userBuffer[1] = 0; - } - - stream_.state = STREAM_CLOSED; - stream_.mode = UNINITIALIZED; -} - -void RtApiPulse::callbackEvent( void ) -{ - PulseAudioHandle *pah = static_cast( stream_.apiHandle ); - - if ( stream_.state == STREAM_STOPPED ) { - MUTEX_LOCK( &stream_.mutex ); - while ( !pah->runnable ) - pthread_cond_wait( &pah->runnable_cv, &stream_.mutex ); - - if ( stream_.state != STREAM_RUNNING ) { - MUTEX_UNLOCK( &stream_.mutex ); - return; - } - MUTEX_UNLOCK( &stream_.mutex ); - } - - if ( stream_.state == STREAM_CLOSED ) { - errorText_ = "RtApiPulse::callbackEvent(): the stream is closed ... " - "this shouldn't happen!"; - error( RtAudioError::WARNING ); - return; - } - - RtAudioCallback callback = (RtAudioCallback) stream_.callbackInfo.callback; - double streamTime = getStreamTime(); - RtAudioStreamStatus status = 0; - int doStopStream = callback( stream_.userBuffer[OUTPUT], stream_.userBuffer[INPUT], - stream_.bufferSize, streamTime, status, - stream_.callbackInfo.userData ); - - if ( doStopStream == 2 ) { - abortStream(); - return; - } - - MUTEX_LOCK( &stream_.mutex ); - void *pulse_in = stream_.doConvertBuffer[INPUT] ? stream_.deviceBuffer : stream_.userBuffer[INPUT]; - void *pulse_out = stream_.doConvertBuffer[OUTPUT] ? stream_.deviceBuffer : stream_.userBuffer[OUTPUT]; - - if ( stream_.state != STREAM_RUNNING ) - goto unlock; - - int pa_error; - size_t bytes; - if (stream_.mode == OUTPUT || stream_.mode == DUPLEX ) { - if ( stream_.doConvertBuffer[OUTPUT] ) { - convertBuffer( stream_.deviceBuffer, - stream_.userBuffer[OUTPUT], - stream_.convertInfo[OUTPUT] ); - bytes = stream_.nDeviceChannels[OUTPUT] * stream_.bufferSize * - formatBytes( stream_.deviceFormat[OUTPUT] ); - } else - bytes = stream_.nUserChannels[OUTPUT] * stream_.bufferSize * - formatBytes( stream_.userFormat ); - - if ( pa_simple_write( pah->s_play, pulse_out, bytes, &pa_error ) < 0 ) { - errorStream_ << "RtApiPulse::callbackEvent: audio write error, " << - pa_strerror( pa_error ) << "."; - errorText_ = errorStream_.str(); - error( RtAudioError::WARNING ); - } - } - - if ( stream_.mode == INPUT || stream_.mode == DUPLEX) { - if ( stream_.doConvertBuffer[INPUT] ) - bytes = stream_.nDeviceChannels[INPUT] * stream_.bufferSize * - formatBytes( stream_.deviceFormat[INPUT] ); - else - bytes = stream_.nUserChannels[INPUT] * stream_.bufferSize * - formatBytes( stream_.userFormat ); - - if ( pa_simple_read( pah->s_rec, pulse_in, bytes, &pa_error ) < 0 ) { - errorStream_ << "RtApiPulse::callbackEvent: audio read error, " << - pa_strerror( pa_error ) << "."; - errorText_ = errorStream_.str(); - error( RtAudioError::WARNING ); - } - if ( stream_.doConvertBuffer[INPUT] ) { - convertBuffer( stream_.userBuffer[INPUT], - stream_.deviceBuffer, - stream_.convertInfo[INPUT] ); - } - } - - unlock: - MUTEX_UNLOCK( &stream_.mutex ); - RtApi::tickStreamTime(); - - if ( doStopStream == 1 ) - stopStream(); -} - -void RtApiPulse::startStream( void ) -{ - PulseAudioHandle *pah = static_cast( stream_.apiHandle ); - - if ( stream_.state == STREAM_CLOSED ) { - errorText_ = "RtApiPulse::startStream(): the stream is not open!"; - error( RtAudioError::INVALID_USE ); - return; - } - if ( stream_.state == STREAM_RUNNING ) { - errorText_ = "RtApiPulse::startStream(): the stream is already running!"; - error( RtAudioError::WARNING ); - return; - } - - MUTEX_LOCK( &stream_.mutex ); - - #if defined( HAVE_GETTIMEOFDAY ) - gettimeofday( &stream_.lastTickTimestamp, NULL ); - #endif - - stream_.state = STREAM_RUNNING; - - pah->runnable = true; - pthread_cond_signal( &pah->runnable_cv ); - MUTEX_UNLOCK( &stream_.mutex ); -} - -void RtApiPulse::stopStream( void ) -{ - PulseAudioHandle *pah = static_cast( stream_.apiHandle ); - - if ( stream_.state == STREAM_CLOSED ) { - errorText_ = "RtApiPulse::stopStream(): the stream is not open!"; - error( RtAudioError::INVALID_USE ); - return; - } - if ( stream_.state == STREAM_STOPPED ) { - errorText_ = "RtApiPulse::stopStream(): the stream is already stopped!"; - error( RtAudioError::WARNING ); - return; - } - - stream_.state = STREAM_STOPPED; - MUTEX_LOCK( &stream_.mutex ); - - if ( pah && pah->s_play ) { - int pa_error; - if ( pa_simple_drain( pah->s_play, &pa_error ) < 0 ) { - errorStream_ << "RtApiPulse::stopStream: error draining output device, " << - pa_strerror( pa_error ) << "."; - errorText_ = errorStream_.str(); - MUTEX_UNLOCK( &stream_.mutex ); - error( RtAudioError::SYSTEM_ERROR ); - return; - } - } - - stream_.state = STREAM_STOPPED; - MUTEX_UNLOCK( &stream_.mutex ); -} - -void RtApiPulse::abortStream( void ) -{ - PulseAudioHandle *pah = static_cast( stream_.apiHandle ); - - if ( stream_.state == STREAM_CLOSED ) { - errorText_ = "RtApiPulse::abortStream(): the stream is not open!"; - error( RtAudioError::INVALID_USE ); - return; - } - if ( stream_.state == STREAM_STOPPED ) { - errorText_ = "RtApiPulse::abortStream(): the stream is already stopped!"; - error( RtAudioError::WARNING ); - return; - } - - stream_.state = STREAM_STOPPED; - MUTEX_LOCK( &stream_.mutex ); - - if ( pah && pah->s_play ) { - int pa_error; - if ( pa_simple_flush( pah->s_play, &pa_error ) < 0 ) { - errorStream_ << "RtApiPulse::abortStream: error flushing output device, " << - pa_strerror( pa_error ) << "."; - errorText_ = errorStream_.str(); - MUTEX_UNLOCK( &stream_.mutex ); - error( RtAudioError::SYSTEM_ERROR ); - return; - } - } - - stream_.state = STREAM_STOPPED; - MUTEX_UNLOCK( &stream_.mutex ); -} - -bool RtApiPulse::probeDeviceOpen( unsigned int device, StreamMode mode, - unsigned int channels, unsigned int firstChannel, - unsigned int sampleRate, RtAudioFormat format, - unsigned int *bufferSize, RtAudio::StreamOptions *options ) -{ - PulseAudioHandle *pah = 0; - unsigned long bufferBytes = 0; - pa_sample_spec ss; - - if ( device != 0 ) return false; - if ( mode != INPUT && mode != OUTPUT ) return false; - if ( channels != 1 && channels != 2 ) { - errorText_ = "RtApiPulse::probeDeviceOpen: unsupported number of channels."; - return false; - } - ss.channels = channels; - - if ( firstChannel != 0 ) return false; - - bool sr_found = false; - for ( const unsigned int *sr = SUPPORTED_SAMPLERATES; *sr; ++sr ) { - if ( sampleRate == *sr ) { - sr_found = true; - stream_.sampleRate = sampleRate; - ss.rate = sampleRate; - break; - } - } - if ( !sr_found ) { - errorText_ = "RtApiPulse::probeDeviceOpen: unsupported sample rate."; - return false; - } - - bool sf_found = 0; - for ( const rtaudio_pa_format_mapping_t *sf = supported_sampleformats; - sf->rtaudio_format && sf->pa_format != PA_SAMPLE_INVALID; ++sf ) { - if ( format == sf->rtaudio_format ) { - sf_found = true; - stream_.userFormat = sf->rtaudio_format; - stream_.deviceFormat[mode] = stream_.userFormat; - ss.format = sf->pa_format; - break; - } - } - if ( !sf_found ) { // Use internal data format conversion. - stream_.userFormat = format; - stream_.deviceFormat[mode] = RTAUDIO_FLOAT32; - ss.format = PA_SAMPLE_FLOAT32LE; - } - - // Set other stream parameters. - if ( options && options->flags & RTAUDIO_NONINTERLEAVED ) stream_.userInterleaved = false; - else stream_.userInterleaved = true; - stream_.deviceInterleaved[mode] = true; - stream_.nBuffers = 1; - stream_.doByteSwap[mode] = false; - stream_.nUserChannels[mode] = channels; - stream_.nDeviceChannels[mode] = channels + firstChannel; - stream_.channelOffset[mode] = 0; - std::string streamName = "RtAudio"; - - // Set flags for buffer conversion. - stream_.doConvertBuffer[mode] = false; - if ( stream_.userFormat != stream_.deviceFormat[mode] ) - stream_.doConvertBuffer[mode] = true; - if ( stream_.nUserChannels[mode] < stream_.nDeviceChannels[mode] ) - stream_.doConvertBuffer[mode] = true; - - // Allocate necessary internal buffers. - bufferBytes = stream_.nUserChannels[mode] * *bufferSize * formatBytes( stream_.userFormat ); - stream_.userBuffer[mode] = (char *) calloc( bufferBytes, 1 ); - if ( stream_.userBuffer[mode] == NULL ) { - errorText_ = "RtApiPulse::probeDeviceOpen: error allocating user buffer memory."; - goto error; - } - stream_.bufferSize = *bufferSize; - - if ( stream_.doConvertBuffer[mode] ) { - - bool makeBuffer = true; - bufferBytes = stream_.nDeviceChannels[mode] * formatBytes( stream_.deviceFormat[mode] ); - if ( mode == INPUT ) { - if ( stream_.mode == OUTPUT && stream_.deviceBuffer ) { - unsigned long bytesOut = stream_.nDeviceChannels[0] * formatBytes( stream_.deviceFormat[0] ); - if ( bufferBytes <= bytesOut ) makeBuffer = false; - } - } - - if ( makeBuffer ) { - bufferBytes *= *bufferSize; - if ( stream_.deviceBuffer ) free( stream_.deviceBuffer ); - stream_.deviceBuffer = (char *) calloc( bufferBytes, 1 ); - if ( stream_.deviceBuffer == NULL ) { - errorText_ = "RtApiPulse::probeDeviceOpen: error allocating device buffer memory."; - goto error; - } - } - } - - stream_.device[mode] = device; - - // Setup the buffer conversion information structure. - if ( stream_.doConvertBuffer[mode] ) setConvertInfo( mode, firstChannel ); - - if ( !stream_.apiHandle ) { - PulseAudioHandle *pah = new PulseAudioHandle; - if ( !pah ) { - errorText_ = "RtApiPulse::probeDeviceOpen: error allocating memory for handle."; - goto error; - } - - stream_.apiHandle = pah; - if ( pthread_cond_init( &pah->runnable_cv, NULL ) != 0 ) { - errorText_ = "RtApiPulse::probeDeviceOpen: error creating condition variable."; - goto error; - } - } - pah = static_cast( stream_.apiHandle ); - - int error; - if ( options && !options->streamName.empty() ) streamName = options->streamName; - switch ( mode ) { - case INPUT: - pa_buffer_attr buffer_attr; - buffer_attr.fragsize = bufferBytes; - buffer_attr.maxlength = -1; - - pah->s_rec = pa_simple_new( NULL, streamName.c_str(), PA_STREAM_RECORD, NULL, "Record", &ss, NULL, &buffer_attr, &error ); - if ( !pah->s_rec ) { - errorText_ = "RtApiPulse::probeDeviceOpen: error connecting input to PulseAudio server."; - goto error; - } - break; - case OUTPUT: - pah->s_play = pa_simple_new( NULL, streamName.c_str(), PA_STREAM_PLAYBACK, NULL, "Playback", &ss, NULL, NULL, &error ); - if ( !pah->s_play ) { - errorText_ = "RtApiPulse::probeDeviceOpen: error connecting output to PulseAudio server."; - goto error; - } - break; - default: - goto error; - } - - if ( stream_.mode == UNINITIALIZED ) - stream_.mode = mode; - else if ( stream_.mode == mode ) - goto error; - else - stream_.mode = DUPLEX; - - if ( !stream_.callbackInfo.isRunning ) { - stream_.callbackInfo.object = this; - - stream_.state = STREAM_STOPPED; - // Set the thread attributes for joinable and realtime scheduling - // priority (optional). The higher priority will only take affect - // if the program is run as root or suid. Note, under Linux - // processes with CAP_SYS_NICE privilege, a user can change - // scheduling policy and priority (thus need not be root). See - // POSIX "capabilities". - pthread_attr_t attr; - pthread_attr_init( &attr ); - pthread_attr_setdetachstate( &attr, PTHREAD_CREATE_JOINABLE ); -#ifdef SCHED_RR // Undefined with some OSes (e.g. NetBSD 1.6.x with GNU Pthread) - if ( options && options->flags & RTAUDIO_SCHEDULE_REALTIME ) { - stream_.callbackInfo.doRealtime = true; - struct sched_param param; - int priority = options->priority; - int min = sched_get_priority_min( SCHED_RR ); - int max = sched_get_priority_max( SCHED_RR ); - if ( priority < min ) priority = min; - else if ( priority > max ) priority = max; - param.sched_priority = priority; - - // Set the policy BEFORE the priority. Otherwise it fails. - pthread_attr_setschedpolicy(&attr, SCHED_RR); - pthread_attr_setscope (&attr, PTHREAD_SCOPE_SYSTEM); - // This is definitely required. Otherwise it fails. - pthread_attr_setinheritsched(&attr, PTHREAD_EXPLICIT_SCHED); - pthread_attr_setschedparam(&attr, ¶m); - } - else - pthread_attr_setschedpolicy( &attr, SCHED_OTHER ); -#else - pthread_attr_setschedpolicy( &attr, SCHED_OTHER ); -#endif - - stream_.callbackInfo.isRunning = true; - int result = pthread_create( &pah->thread, &attr, pulseaudio_callback, (void *)&stream_.callbackInfo); - pthread_attr_destroy(&attr); - if(result != 0) { - // Failed. Try instead with default attributes. - result = pthread_create( &pah->thread, NULL, pulseaudio_callback, (void *)&stream_.callbackInfo); - if(result != 0) { - stream_.callbackInfo.isRunning = false; - errorText_ = "RtApiPulse::probeDeviceOpen: error creating thread."; - goto error; - } - } - } - - return SUCCESS; - - error: - if ( pah && stream_.callbackInfo.isRunning ) { - pthread_cond_destroy( &pah->runnable_cv ); - delete pah; - stream_.apiHandle = 0; - } - - for ( int i=0; i<2; i++ ) { - if ( stream_.userBuffer[i] ) { - free( stream_.userBuffer[i] ); - stream_.userBuffer[i] = 0; - } - } - - if ( stream_.deviceBuffer ) { - free( stream_.deviceBuffer ); - stream_.deviceBuffer = 0; - } - - stream_.state = STREAM_CLOSED; - return FAILURE; -} - -//******************** End of __LINUX_PULSE__ *********************// -#endif - -#if defined(__LINUX_OSS__) - -#include -#include -#include -#include -#include -#include -#include - -static void *ossCallbackHandler(void * ptr); - -// A structure to hold various information related to the OSS API -// implementation. -struct OssHandle { - int id[2]; // device ids - bool xrun[2]; - bool triggered; - pthread_cond_t runnable; - - OssHandle() - :triggered(false) { id[0] = 0; id[1] = 0; xrun[0] = false; xrun[1] = false; } -}; - -RtApiOss :: RtApiOss() -{ - // Nothing to do here. -} - -RtApiOss :: ~RtApiOss() -{ - if ( stream_.state != STREAM_CLOSED ) closeStream(); -} - -unsigned int RtApiOss :: getDeviceCount( void ) -{ - int mixerfd = open( "/dev/mixer", O_RDWR, 0 ); - if ( mixerfd == -1 ) { - errorText_ = "RtApiOss::getDeviceCount: error opening '/dev/mixer'."; - error( RtAudioError::WARNING ); - return 0; - } - - oss_sysinfo sysinfo; - if ( ioctl( mixerfd, SNDCTL_SYSINFO, &sysinfo ) == -1 ) { - close( mixerfd ); - errorText_ = "RtApiOss::getDeviceCount: error getting sysinfo, OSS version >= 4.0 is required."; - error( RtAudioError::WARNING ); - return 0; - } - - close( mixerfd ); - return sysinfo.numaudios; -} - -RtAudio::DeviceInfo RtApiOss :: getDeviceInfo( unsigned int device ) -{ - RtAudio::DeviceInfo info; - info.probed = false; - - int mixerfd = open( "/dev/mixer", O_RDWR, 0 ); - if ( mixerfd == -1 ) { - errorText_ = "RtApiOss::getDeviceInfo: error opening '/dev/mixer'."; - error( RtAudioError::WARNING ); - return info; - } - - oss_sysinfo sysinfo; - int result = ioctl( mixerfd, SNDCTL_SYSINFO, &sysinfo ); - if ( result == -1 ) { - close( mixerfd ); - errorText_ = "RtApiOss::getDeviceInfo: error getting sysinfo, OSS version >= 4.0 is required."; - error( RtAudioError::WARNING ); - return info; - } - - unsigned nDevices = sysinfo.numaudios; - if ( nDevices == 0 ) { - close( mixerfd ); - errorText_ = "RtApiOss::getDeviceInfo: no devices found!"; - error( RtAudioError::INVALID_USE ); - return info; - } - - if ( device >= nDevices ) { - close( mixerfd ); - errorText_ = "RtApiOss::getDeviceInfo: device ID is invalid!"; - error( RtAudioError::INVALID_USE ); - return info; - } - - oss_audioinfo ainfo; - ainfo.dev = device; - result = ioctl( mixerfd, SNDCTL_AUDIOINFO, &ainfo ); - close( mixerfd ); - if ( result == -1 ) { - errorStream_ << "RtApiOss::getDeviceInfo: error getting device (" << ainfo.name << ") info."; - errorText_ = errorStream_.str(); - error( RtAudioError::WARNING ); - return info; - } - - // Probe channels - if ( ainfo.caps & PCM_CAP_OUTPUT ) info.outputChannels = ainfo.max_channels; - if ( ainfo.caps & PCM_CAP_INPUT ) info.inputChannels = ainfo.max_channels; - if ( ainfo.caps & PCM_CAP_DUPLEX ) { - if ( info.outputChannels > 0 && info.inputChannels > 0 && ainfo.caps & PCM_CAP_DUPLEX ) - info.duplexChannels = (info.outputChannels > info.inputChannels) ? info.inputChannels : info.outputChannels; - } - - // Probe data formats ... do for input - unsigned long mask = ainfo.iformats; - if ( mask & AFMT_S16_LE || mask & AFMT_S16_BE ) - info.nativeFormats |= RTAUDIO_SINT16; - if ( mask & AFMT_S8 ) - info.nativeFormats |= RTAUDIO_SINT8; - if ( mask & AFMT_S32_LE || mask & AFMT_S32_BE ) - info.nativeFormats |= RTAUDIO_SINT32; -#ifdef AFMT_FLOAT - if ( mask & AFMT_FLOAT ) - info.nativeFormats |= RTAUDIO_FLOAT32; -#endif - if ( mask & AFMT_S24_LE || mask & AFMT_S24_BE ) - info.nativeFormats |= RTAUDIO_SINT24; - - // Check that we have at least one supported format - if ( info.nativeFormats == 0 ) { - errorStream_ << "RtApiOss::getDeviceInfo: device (" << ainfo.name << ") data format not supported by RtAudio."; - errorText_ = errorStream_.str(); - error( RtAudioError::WARNING ); - return info; - } - - // Probe the supported sample rates. - info.sampleRates.clear(); - if ( ainfo.nrates ) { - for ( unsigned int i=0; i info.preferredSampleRate ) ) - info.preferredSampleRate = SAMPLE_RATES[k]; - - break; - } - } - } - } - else { - // Check min and max rate values; - for ( unsigned int k=0; k= (int) SAMPLE_RATES[k] ) { - info.sampleRates.push_back( SAMPLE_RATES[k] ); - - if ( !info.preferredSampleRate || ( SAMPLE_RATES[k] <= 48000 && SAMPLE_RATES[k] > info.preferredSampleRate ) ) - info.preferredSampleRate = SAMPLE_RATES[k]; - } - } - } - - if ( info.sampleRates.size() == 0 ) { - errorStream_ << "RtApiOss::getDeviceInfo: no supported sample rates found for device (" << ainfo.name << ")."; - errorText_ = errorStream_.str(); - error( RtAudioError::WARNING ); - } - else { - info.probed = true; - info.name = ainfo.name; - } - - return info; -} - - -bool RtApiOss :: probeDeviceOpen( unsigned int device, StreamMode mode, unsigned int channels, - unsigned int firstChannel, unsigned int sampleRate, - RtAudioFormat format, unsigned int *bufferSize, - RtAudio::StreamOptions *options ) -{ - int mixerfd = open( "/dev/mixer", O_RDWR, 0 ); - if ( mixerfd == -1 ) { - errorText_ = "RtApiOss::probeDeviceOpen: error opening '/dev/mixer'."; - return FAILURE; - } - - oss_sysinfo sysinfo; - int result = ioctl( mixerfd, SNDCTL_SYSINFO, &sysinfo ); - if ( result == -1 ) { - close( mixerfd ); - errorText_ = "RtApiOss::probeDeviceOpen: error getting sysinfo, OSS version >= 4.0 is required."; - return FAILURE; - } - - unsigned nDevices = sysinfo.numaudios; - if ( nDevices == 0 ) { - // This should not happen because a check is made before this function is called. - close( mixerfd ); - errorText_ = "RtApiOss::probeDeviceOpen: no devices found!"; - return FAILURE; - } - - if ( device >= nDevices ) { - // This should not happen because a check is made before this function is called. - close( mixerfd ); - errorText_ = "RtApiOss::probeDeviceOpen: device ID is invalid!"; - return FAILURE; - } - - oss_audioinfo ainfo; - ainfo.dev = device; - result = ioctl( mixerfd, SNDCTL_AUDIOINFO, &ainfo ); - close( mixerfd ); - if ( result == -1 ) { - errorStream_ << "RtApiOss::getDeviceInfo: error getting device (" << ainfo.name << ") info."; - errorText_ = errorStream_.str(); - return FAILURE; - } - - // Check if device supports input or output - if ( ( mode == OUTPUT && !( ainfo.caps & PCM_CAP_OUTPUT ) ) || - ( mode == INPUT && !( ainfo.caps & PCM_CAP_INPUT ) ) ) { - if ( mode == OUTPUT ) - errorStream_ << "RtApiOss::probeDeviceOpen: device (" << ainfo.name << ") does not support output."; - else - errorStream_ << "RtApiOss::probeDeviceOpen: device (" << ainfo.name << ") does not support input."; - errorText_ = errorStream_.str(); - return FAILURE; - } - - int flags = 0; - OssHandle *handle = (OssHandle *) stream_.apiHandle; - if ( mode == OUTPUT ) - flags |= O_WRONLY; - else { // mode == INPUT - if (stream_.mode == OUTPUT && stream_.device[0] == device) { - // We just set the same device for playback ... close and reopen for duplex (OSS only). - close( handle->id[0] ); - handle->id[0] = 0; - if ( !( ainfo.caps & PCM_CAP_DUPLEX ) ) { - errorStream_ << "RtApiOss::probeDeviceOpen: device (" << ainfo.name << ") does not support duplex mode."; - errorText_ = errorStream_.str(); - return FAILURE; - } - // Check that the number previously set channels is the same. - if ( stream_.nUserChannels[0] != channels ) { - errorStream_ << "RtApiOss::probeDeviceOpen: input/output channels must be equal for OSS duplex device (" << ainfo.name << ")."; - errorText_ = errorStream_.str(); - return FAILURE; - } - flags |= O_RDWR; - } - else - flags |= O_RDONLY; - } - - // Set exclusive access if specified. - if ( options && options->flags & RTAUDIO_HOG_DEVICE ) flags |= O_EXCL; - - // Try to open the device. - int fd; - fd = open( ainfo.devnode, flags, 0 ); - if ( fd == -1 ) { - if ( errno == EBUSY ) - errorStream_ << "RtApiOss::probeDeviceOpen: device (" << ainfo.name << ") is busy."; - else - errorStream_ << "RtApiOss::probeDeviceOpen: error opening device (" << ainfo.name << ")."; - errorText_ = errorStream_.str(); - return FAILURE; - } - - // For duplex operation, specifically set this mode (this doesn't seem to work). - /* - if ( flags | O_RDWR ) { - result = ioctl( fd, SNDCTL_DSP_SETDUPLEX, NULL ); - if ( result == -1) { - errorStream_ << "RtApiOss::probeDeviceOpen: error setting duplex mode for device (" << ainfo.name << ")."; - errorText_ = errorStream_.str(); - return FAILURE; - } - } - */ - - // Check the device channel support. - stream_.nUserChannels[mode] = channels; - if ( ainfo.max_channels < (int)(channels + firstChannel) ) { - close( fd ); - errorStream_ << "RtApiOss::probeDeviceOpen: the device (" << ainfo.name << ") does not support requested channel parameters."; - errorText_ = errorStream_.str(); - return FAILURE; - } - - // Set the number of channels. - int deviceChannels = channels + firstChannel; - result = ioctl( fd, SNDCTL_DSP_CHANNELS, &deviceChannels ); - if ( result == -1 || deviceChannels < (int)(channels + firstChannel) ) { - close( fd ); - errorStream_ << "RtApiOss::probeDeviceOpen: error setting channel parameters on device (" << ainfo.name << ")."; - errorText_ = errorStream_.str(); - return FAILURE; - } - stream_.nDeviceChannels[mode] = deviceChannels; - - // Get the data format mask - int mask; - result = ioctl( fd, SNDCTL_DSP_GETFMTS, &mask ); - if ( result == -1 ) { - close( fd ); - errorStream_ << "RtApiOss::probeDeviceOpen: error getting device (" << ainfo.name << ") data formats."; - errorText_ = errorStream_.str(); - return FAILURE; - } - - // Determine how to set the device format. - stream_.userFormat = format; - int deviceFormat = -1; - stream_.doByteSwap[mode] = false; - if ( format == RTAUDIO_SINT8 ) { - if ( mask & AFMT_S8 ) { - deviceFormat = AFMT_S8; - stream_.deviceFormat[mode] = RTAUDIO_SINT8; - } - } - else if ( format == RTAUDIO_SINT16 ) { - if ( mask & AFMT_S16_NE ) { - deviceFormat = AFMT_S16_NE; - stream_.deviceFormat[mode] = RTAUDIO_SINT16; - } - else if ( mask & AFMT_S16_OE ) { - deviceFormat = AFMT_S16_OE; - stream_.deviceFormat[mode] = RTAUDIO_SINT16; - stream_.doByteSwap[mode] = true; - } - } - else if ( format == RTAUDIO_SINT24 ) { - if ( mask & AFMT_S24_NE ) { - deviceFormat = AFMT_S24_NE; - stream_.deviceFormat[mode] = RTAUDIO_SINT24; - } - else if ( mask & AFMT_S24_OE ) { - deviceFormat = AFMT_S24_OE; - stream_.deviceFormat[mode] = RTAUDIO_SINT24; - stream_.doByteSwap[mode] = true; - } - } - else if ( format == RTAUDIO_SINT32 ) { - if ( mask & AFMT_S32_NE ) { - deviceFormat = AFMT_S32_NE; - stream_.deviceFormat[mode] = RTAUDIO_SINT32; - } - else if ( mask & AFMT_S32_OE ) { - deviceFormat = AFMT_S32_OE; - stream_.deviceFormat[mode] = RTAUDIO_SINT32; - stream_.doByteSwap[mode] = true; - } - } - - if ( deviceFormat == -1 ) { - // The user requested format is not natively supported by the device. - if ( mask & AFMT_S16_NE ) { - deviceFormat = AFMT_S16_NE; - stream_.deviceFormat[mode] = RTAUDIO_SINT16; - } - else if ( mask & AFMT_S32_NE ) { - deviceFormat = AFMT_S32_NE; - stream_.deviceFormat[mode] = RTAUDIO_SINT32; - } - else if ( mask & AFMT_S24_NE ) { - deviceFormat = AFMT_S24_NE; - stream_.deviceFormat[mode] = RTAUDIO_SINT24; - } - else if ( mask & AFMT_S16_OE ) { - deviceFormat = AFMT_S16_OE; - stream_.deviceFormat[mode] = RTAUDIO_SINT16; - stream_.doByteSwap[mode] = true; - } - else if ( mask & AFMT_S32_OE ) { - deviceFormat = AFMT_S32_OE; - stream_.deviceFormat[mode] = RTAUDIO_SINT32; - stream_.doByteSwap[mode] = true; - } - else if ( mask & AFMT_S24_OE ) { - deviceFormat = AFMT_S24_OE; - stream_.deviceFormat[mode] = RTAUDIO_SINT24; - stream_.doByteSwap[mode] = true; - } - else if ( mask & AFMT_S8) { - deviceFormat = AFMT_S8; - stream_.deviceFormat[mode] = RTAUDIO_SINT8; - } - } - - if ( stream_.deviceFormat[mode] == 0 ) { - // This really shouldn't happen ... - close( fd ); - errorStream_ << "RtApiOss::probeDeviceOpen: device (" << ainfo.name << ") data format not supported by RtAudio."; - errorText_ = errorStream_.str(); - return FAILURE; - } - - // Set the data format. - int temp = deviceFormat; - result = ioctl( fd, SNDCTL_DSP_SETFMT, &deviceFormat ); - if ( result == -1 || deviceFormat != temp ) { - close( fd ); - errorStream_ << "RtApiOss::probeDeviceOpen: error setting data format on device (" << ainfo.name << ")."; - errorText_ = errorStream_.str(); - return FAILURE; - } - - // Attempt to set the buffer size. According to OSS, the minimum - // number of buffers is two. The supposed minimum buffer size is 16 - // bytes, so that will be our lower bound. The argument to this - // call is in the form 0xMMMMSSSS (hex), where the buffer size (in - // bytes) is given as 2^SSSS and the number of buffers as 2^MMMM. - // We'll check the actual value used near the end of the setup - // procedure. - int ossBufferBytes = *bufferSize * formatBytes( stream_.deviceFormat[mode] ) * deviceChannels; - if ( ossBufferBytes < 16 ) ossBufferBytes = 16; - int buffers = 0; - if ( options ) buffers = options->numberOfBuffers; - if ( options && options->flags & RTAUDIO_MINIMIZE_LATENCY ) buffers = 2; - if ( buffers < 2 ) buffers = 3; - temp = ((int) buffers << 16) + (int)( log10( (double)ossBufferBytes ) / log10( 2.0 ) ); - result = ioctl( fd, SNDCTL_DSP_SETFRAGMENT, &temp ); - if ( result == -1 ) { - close( fd ); - errorStream_ << "RtApiOss::probeDeviceOpen: error setting buffer size on device (" << ainfo.name << ")."; - errorText_ = errorStream_.str(); - return FAILURE; - } - stream_.nBuffers = buffers; - - // Save buffer size (in sample frames). - *bufferSize = ossBufferBytes / ( formatBytes(stream_.deviceFormat[mode]) * deviceChannels ); - stream_.bufferSize = *bufferSize; - - // Set the sample rate. - int srate = sampleRate; - result = ioctl( fd, SNDCTL_DSP_SPEED, &srate ); - if ( result == -1 ) { - close( fd ); - errorStream_ << "RtApiOss::probeDeviceOpen: error setting sample rate (" << sampleRate << ") on device (" << ainfo.name << ")."; - errorText_ = errorStream_.str(); - return FAILURE; - } - - // Verify the sample rate setup worked. - if ( abs( srate - (int)sampleRate ) > 100 ) { - close( fd ); - errorStream_ << "RtApiOss::probeDeviceOpen: device (" << ainfo.name << ") does not support sample rate (" << sampleRate << ")."; - errorText_ = errorStream_.str(); - return FAILURE; - } - stream_.sampleRate = sampleRate; - - if ( mode == INPUT && stream_.mode == OUTPUT && stream_.device[0] == device) { - // We're doing duplex setup here. - stream_.deviceFormat[0] = stream_.deviceFormat[1]; - stream_.nDeviceChannels[0] = deviceChannels; - } - - // Set interleaving parameters. - stream_.userInterleaved = true; - stream_.deviceInterleaved[mode] = true; - if ( options && options->flags & RTAUDIO_NONINTERLEAVED ) - stream_.userInterleaved = false; - - // Set flags for buffer conversion - stream_.doConvertBuffer[mode] = false; - if ( stream_.userFormat != stream_.deviceFormat[mode] ) - stream_.doConvertBuffer[mode] = true; - if ( stream_.nUserChannels[mode] < stream_.nDeviceChannels[mode] ) - stream_.doConvertBuffer[mode] = true; - if ( stream_.userInterleaved != stream_.deviceInterleaved[mode] && - stream_.nUserChannels[mode] > 1 ) - stream_.doConvertBuffer[mode] = true; - - // Allocate the stream handles if necessary and then save. - if ( stream_.apiHandle == 0 ) { - try { - handle = new OssHandle; - } - catch ( std::bad_alloc& ) { - errorText_ = "RtApiOss::probeDeviceOpen: error allocating OssHandle memory."; - goto error; - } - - if ( pthread_cond_init( &handle->runnable, NULL ) ) { - errorText_ = "RtApiOss::probeDeviceOpen: error initializing pthread condition variable."; - goto error; - } - - stream_.apiHandle = (void *) handle; - } - else { - handle = (OssHandle *) stream_.apiHandle; - } - handle->id[mode] = fd; - - // Allocate necessary internal buffers. - unsigned long bufferBytes; - bufferBytes = stream_.nUserChannels[mode] * *bufferSize * formatBytes( stream_.userFormat ); - stream_.userBuffer[mode] = (char *) calloc( bufferBytes, 1 ); - if ( stream_.userBuffer[mode] == NULL ) { - errorText_ = "RtApiOss::probeDeviceOpen: error allocating user buffer memory."; - goto error; - } - - if ( stream_.doConvertBuffer[mode] ) { - - bool makeBuffer = true; - bufferBytes = stream_.nDeviceChannels[mode] * formatBytes( stream_.deviceFormat[mode] ); - if ( mode == INPUT ) { - if ( stream_.mode == OUTPUT && stream_.deviceBuffer ) { - unsigned long bytesOut = stream_.nDeviceChannels[0] * formatBytes( stream_.deviceFormat[0] ); - if ( bufferBytes <= bytesOut ) makeBuffer = false; - } - } - - if ( makeBuffer ) { - bufferBytes *= *bufferSize; - if ( stream_.deviceBuffer ) free( stream_.deviceBuffer ); - stream_.deviceBuffer = (char *) calloc( bufferBytes, 1 ); - if ( stream_.deviceBuffer == NULL ) { - errorText_ = "RtApiOss::probeDeviceOpen: error allocating device buffer memory."; - goto error; - } - } - } - - stream_.device[mode] = device; - stream_.state = STREAM_STOPPED; - - // Setup the buffer conversion information structure. - if ( stream_.doConvertBuffer[mode] ) setConvertInfo( mode, firstChannel ); - - // Setup thread if necessary. - if ( stream_.mode == OUTPUT && mode == INPUT ) { - // We had already set up an output stream. - stream_.mode = DUPLEX; - if ( stream_.device[0] == device ) handle->id[0] = fd; - } - else { - stream_.mode = mode; - - // Setup callback thread. - stream_.callbackInfo.object = (void *) this; - - // Set the thread attributes for joinable and realtime scheduling - // priority. The higher priority will only take affect if the - // program is run as root or suid. - pthread_attr_t attr; - pthread_attr_init( &attr ); - pthread_attr_setdetachstate( &attr, PTHREAD_CREATE_JOINABLE ); -#ifdef SCHED_RR // Undefined with some OSes (e.g. NetBSD 1.6.x with GNU Pthread) - if ( options && options->flags & RTAUDIO_SCHEDULE_REALTIME ) { - stream_.callbackInfo.doRealtime = true; - struct sched_param param; - int priority = options->priority; - int min = sched_get_priority_min( SCHED_RR ); - int max = sched_get_priority_max( SCHED_RR ); - if ( priority < min ) priority = min; - else if ( priority > max ) priority = max; - param.sched_priority = priority; - - // Set the policy BEFORE the priority. Otherwise it fails. - pthread_attr_setschedpolicy(&attr, SCHED_RR); - pthread_attr_setscope (&attr, PTHREAD_SCOPE_SYSTEM); - // This is definitely required. Otherwise it fails. - pthread_attr_setinheritsched(&attr, PTHREAD_EXPLICIT_SCHED); - pthread_attr_setschedparam(&attr, ¶m); - } - else - pthread_attr_setschedpolicy( &attr, SCHED_OTHER ); -#else - pthread_attr_setschedpolicy( &attr, SCHED_OTHER ); -#endif - - stream_.callbackInfo.isRunning = true; - result = pthread_create( &stream_.callbackInfo.thread, &attr, ossCallbackHandler, &stream_.callbackInfo ); - pthread_attr_destroy( &attr ); - if ( result ) { - // Failed. Try instead with default attributes. - result = pthread_create( &stream_.callbackInfo.thread, NULL, ossCallbackHandler, &stream_.callbackInfo ); - if ( result ) { - stream_.callbackInfo.isRunning = false; - errorText_ = "RtApiOss::error creating callback thread!"; - goto error; - } - } - } - - return SUCCESS; - - error: - if ( handle ) { - pthread_cond_destroy( &handle->runnable ); - if ( handle->id[0] ) close( handle->id[0] ); - if ( handle->id[1] ) close( handle->id[1] ); - delete handle; - stream_.apiHandle = 0; - } - - for ( int i=0; i<2; i++ ) { - if ( stream_.userBuffer[i] ) { - free( stream_.userBuffer[i] ); - stream_.userBuffer[i] = 0; - } - } - - if ( stream_.deviceBuffer ) { - free( stream_.deviceBuffer ); - stream_.deviceBuffer = 0; - } - - stream_.state = STREAM_CLOSED; - return FAILURE; -} - -void RtApiOss :: closeStream() -{ - if ( stream_.state == STREAM_CLOSED ) { - errorText_ = "RtApiOss::closeStream(): no open stream to close!"; - error( RtAudioError::WARNING ); - return; - } - - OssHandle *handle = (OssHandle *) stream_.apiHandle; - stream_.callbackInfo.isRunning = false; - MUTEX_LOCK( &stream_.mutex ); - if ( stream_.state == STREAM_STOPPED ) - pthread_cond_signal( &handle->runnable ); - MUTEX_UNLOCK( &stream_.mutex ); - pthread_join( stream_.callbackInfo.thread, NULL ); - - if ( stream_.state == STREAM_RUNNING ) { - if ( stream_.mode == OUTPUT || stream_.mode == DUPLEX ) - ioctl( handle->id[0], SNDCTL_DSP_HALT, 0 ); - else - ioctl( handle->id[1], SNDCTL_DSP_HALT, 0 ); - stream_.state = STREAM_STOPPED; - } - - if ( handle ) { - pthread_cond_destroy( &handle->runnable ); - if ( handle->id[0] ) close( handle->id[0] ); - if ( handle->id[1] ) close( handle->id[1] ); - delete handle; - stream_.apiHandle = 0; - } - - for ( int i=0; i<2; i++ ) { - if ( stream_.userBuffer[i] ) { - free( stream_.userBuffer[i] ); - stream_.userBuffer[i] = 0; - } - } - - if ( stream_.deviceBuffer ) { - free( stream_.deviceBuffer ); - stream_.deviceBuffer = 0; - } - - stream_.mode = UNINITIALIZED; - stream_.state = STREAM_CLOSED; -} - -void RtApiOss :: startStream() -{ - verifyStream(); - if ( stream_.state == STREAM_RUNNING ) { - errorText_ = "RtApiOss::startStream(): the stream is already running!"; - error( RtAudioError::WARNING ); - return; - } - - MUTEX_LOCK( &stream_.mutex ); - - #if defined( HAVE_GETTIMEOFDAY ) - gettimeofday( &stream_.lastTickTimestamp, NULL ); - #endif - - stream_.state = STREAM_RUNNING; - - // No need to do anything else here ... OSS automatically starts - // when fed samples. - - MUTEX_UNLOCK( &stream_.mutex ); - - OssHandle *handle = (OssHandle *) stream_.apiHandle; - pthread_cond_signal( &handle->runnable ); -} - -void RtApiOss :: stopStream() -{ - verifyStream(); - if ( stream_.state == STREAM_STOPPED ) { - errorText_ = "RtApiOss::stopStream(): the stream is already stopped!"; - error( RtAudioError::WARNING ); - return; - } - - MUTEX_LOCK( &stream_.mutex ); - - // The state might change while waiting on a mutex. - if ( stream_.state == STREAM_STOPPED ) { - MUTEX_UNLOCK( &stream_.mutex ); - return; - } - - int result = 0; - OssHandle *handle = (OssHandle *) stream_.apiHandle; - if ( stream_.mode == OUTPUT || stream_.mode == DUPLEX ) { - - // Flush the output with zeros a few times. - char *buffer; - int samples; - RtAudioFormat format; - - if ( stream_.doConvertBuffer[0] ) { - buffer = stream_.deviceBuffer; - samples = stream_.bufferSize * stream_.nDeviceChannels[0]; - format = stream_.deviceFormat[0]; - } - else { - buffer = stream_.userBuffer[0]; - samples = stream_.bufferSize * stream_.nUserChannels[0]; - format = stream_.userFormat; - } - - memset( buffer, 0, samples * formatBytes(format) ); - for ( unsigned int i=0; iid[0], buffer, samples * formatBytes(format) ); - if ( result == -1 ) { - errorText_ = "RtApiOss::stopStream: audio write error."; - error( RtAudioError::WARNING ); - } - } - - result = ioctl( handle->id[0], SNDCTL_DSP_HALT, 0 ); - if ( result == -1 ) { - errorStream_ << "RtApiOss::stopStream: system error stopping callback procedure on device (" << stream_.device[0] << ")."; - errorText_ = errorStream_.str(); - goto unlock; - } - handle->triggered = false; - } - - if ( stream_.mode == INPUT || ( stream_.mode == DUPLEX && handle->id[0] != handle->id[1] ) ) { - result = ioctl( handle->id[1], SNDCTL_DSP_HALT, 0 ); - if ( result == -1 ) { - errorStream_ << "RtApiOss::stopStream: system error stopping input callback procedure on device (" << stream_.device[0] << ")."; - errorText_ = errorStream_.str(); - goto unlock; - } - } - - unlock: - stream_.state = STREAM_STOPPED; - MUTEX_UNLOCK( &stream_.mutex ); - - if ( result != -1 ) return; - error( RtAudioError::SYSTEM_ERROR ); -} - -void RtApiOss :: abortStream() -{ - verifyStream(); - if ( stream_.state == STREAM_STOPPED ) { - errorText_ = "RtApiOss::abortStream(): the stream is already stopped!"; - error( RtAudioError::WARNING ); - return; - } - - MUTEX_LOCK( &stream_.mutex ); - - // The state might change while waiting on a mutex. - if ( stream_.state == STREAM_STOPPED ) { - MUTEX_UNLOCK( &stream_.mutex ); - return; - } - - int result = 0; - OssHandle *handle = (OssHandle *) stream_.apiHandle; - if ( stream_.mode == OUTPUT || stream_.mode == DUPLEX ) { - result = ioctl( handle->id[0], SNDCTL_DSP_HALT, 0 ); - if ( result == -1 ) { - errorStream_ << "RtApiOss::abortStream: system error stopping callback procedure on device (" << stream_.device[0] << ")."; - errorText_ = errorStream_.str(); - goto unlock; - } - handle->triggered = false; - } - - if ( stream_.mode == INPUT || ( stream_.mode == DUPLEX && handle->id[0] != handle->id[1] ) ) { - result = ioctl( handle->id[1], SNDCTL_DSP_HALT, 0 ); - if ( result == -1 ) { - errorStream_ << "RtApiOss::abortStream: system error stopping input callback procedure on device (" << stream_.device[0] << ")."; - errorText_ = errorStream_.str(); - goto unlock; - } - } - - unlock: - stream_.state = STREAM_STOPPED; - MUTEX_UNLOCK( &stream_.mutex ); - - if ( result != -1 ) return; - error( RtAudioError::SYSTEM_ERROR ); -} - -void RtApiOss :: callbackEvent() -{ - OssHandle *handle = (OssHandle *) stream_.apiHandle; - if ( stream_.state == STREAM_STOPPED ) { - MUTEX_LOCK( &stream_.mutex ); - pthread_cond_wait( &handle->runnable, &stream_.mutex ); - if ( stream_.state != STREAM_RUNNING ) { - MUTEX_UNLOCK( &stream_.mutex ); - return; - } - MUTEX_UNLOCK( &stream_.mutex ); - } - - if ( stream_.state == STREAM_CLOSED ) { - errorText_ = "RtApiOss::callbackEvent(): the stream is closed ... this shouldn't happen!"; - error( RtAudioError::WARNING ); - return; - } - - // Invoke user callback to get fresh output data. - int doStopStream = 0; - RtAudioCallback callback = (RtAudioCallback) stream_.callbackInfo.callback; - double streamTime = getStreamTime(); - RtAudioStreamStatus status = 0; - if ( stream_.mode != INPUT && handle->xrun[0] == true ) { - status |= RTAUDIO_OUTPUT_UNDERFLOW; - handle->xrun[0] = false; - } - if ( stream_.mode != OUTPUT && handle->xrun[1] == true ) { - status |= RTAUDIO_INPUT_OVERFLOW; - handle->xrun[1] = false; - } - doStopStream = callback( stream_.userBuffer[0], stream_.userBuffer[1], - stream_.bufferSize, streamTime, status, stream_.callbackInfo.userData ); - if ( doStopStream == 2 ) { - this->abortStream(); - return; - } - - MUTEX_LOCK( &stream_.mutex ); - - // The state might change while waiting on a mutex. - if ( stream_.state == STREAM_STOPPED ) goto unlock; - - int result; - char *buffer; - int samples; - RtAudioFormat format; - - if ( stream_.mode == OUTPUT || stream_.mode == DUPLEX ) { - - // Setup parameters and do buffer conversion if necessary. - if ( stream_.doConvertBuffer[0] ) { - buffer = stream_.deviceBuffer; - convertBuffer( buffer, stream_.userBuffer[0], stream_.convertInfo[0] ); - samples = stream_.bufferSize * stream_.nDeviceChannels[0]; - format = stream_.deviceFormat[0]; - } - else { - buffer = stream_.userBuffer[0]; - samples = stream_.bufferSize * stream_.nUserChannels[0]; - format = stream_.userFormat; - } - - // Do byte swapping if necessary. - if ( stream_.doByteSwap[0] ) - byteSwapBuffer( buffer, samples, format ); - - if ( stream_.mode == DUPLEX && handle->triggered == false ) { - int trig = 0; - ioctl( handle->id[0], SNDCTL_DSP_SETTRIGGER, &trig ); - result = write( handle->id[0], buffer, samples * formatBytes(format) ); - trig = PCM_ENABLE_INPUT|PCM_ENABLE_OUTPUT; - ioctl( handle->id[0], SNDCTL_DSP_SETTRIGGER, &trig ); - handle->triggered = true; - } - else - // Write samples to device. - result = write( handle->id[0], buffer, samples * formatBytes(format) ); - - if ( result == -1 ) { - // We'll assume this is an underrun, though there isn't a - // specific means for determining that. - handle->xrun[0] = true; - errorText_ = "RtApiOss::callbackEvent: audio write error."; - error( RtAudioError::WARNING ); - // Continue on to input section. - } - } - - if ( stream_.mode == INPUT || stream_.mode == DUPLEX ) { - - // Setup parameters. - if ( stream_.doConvertBuffer[1] ) { - buffer = stream_.deviceBuffer; - samples = stream_.bufferSize * stream_.nDeviceChannels[1]; - format = stream_.deviceFormat[1]; - } - else { - buffer = stream_.userBuffer[1]; - samples = stream_.bufferSize * stream_.nUserChannels[1]; - format = stream_.userFormat; - } - - // Read samples from device. - result = read( handle->id[1], buffer, samples * formatBytes(format) ); - - if ( result == -1 ) { - // We'll assume this is an overrun, though there isn't a - // specific means for determining that. - handle->xrun[1] = true; - errorText_ = "RtApiOss::callbackEvent: audio read error."; - error( RtAudioError::WARNING ); - goto unlock; - } - - // Do byte swapping if necessary. - if ( stream_.doByteSwap[1] ) - byteSwapBuffer( buffer, samples, format ); - - // Do buffer conversion if necessary. - if ( stream_.doConvertBuffer[1] ) - convertBuffer( stream_.userBuffer[1], stream_.deviceBuffer, stream_.convertInfo[1] ); - } - - unlock: - MUTEX_UNLOCK( &stream_.mutex ); - - RtApi::tickStreamTime(); - if ( doStopStream == 1 ) this->stopStream(); -} - -static void *ossCallbackHandler( void *ptr ) -{ - CallbackInfo *info = (CallbackInfo *) ptr; - RtApiOss *object = (RtApiOss *) info->object; - bool *isRunning = &info->isRunning; - -#ifdef SCHED_RR // Undefined with some OSes (e.g. NetBSD 1.6.x with GNU Pthread) - if (info->doRealtime) { - std::cerr << "RtAudio oss: " << - (sched_getscheduler(0) == SCHED_RR ? "" : "_NOT_ ") << - "running realtime scheduling" << std::endl; - } -#endif - - while ( *isRunning == true ) { - pthread_testcancel(); - object->callbackEvent(); - } - - pthread_exit( NULL ); -} - -//******************** End of __LINUX_OSS__ *********************// -#endif - - -// *************************************************** // -// -// Protected common (OS-independent) RtAudio methods. -// -// *************************************************** // - -// This method can be modified to control the behavior of error -// message printing. -void RtApi :: error( RtAudioError::Type type ) -{ - errorStream_.str(""); // clear the ostringstream - - RtAudioErrorCallback errorCallback = (RtAudioErrorCallback) stream_.callbackInfo.errorCallback; - if ( errorCallback ) { - // abortStream() can generate new error messages. Ignore them. Just keep original one. - - if ( firstErrorOccurred_ ) - return; - - firstErrorOccurred_ = true; - const std::string errorMessage = errorText_; - - if ( type != RtAudioError::WARNING && stream_.state != STREAM_STOPPED) { - stream_.callbackInfo.isRunning = false; // exit from the thread - abortStream(); - } - - errorCallback( type, errorMessage ); - firstErrorOccurred_ = false; - return; - } - - if ( type == RtAudioError::WARNING && showWarnings_ == true ) - std::cerr << '\n' << errorText_ << "\n\n"; - else if ( type != RtAudioError::WARNING ) - throw( RtAudioError( errorText_, type ) ); -} - -void RtApi :: verifyStream() -{ - if ( stream_.state == STREAM_CLOSED ) { - errorText_ = "RtApi:: a stream is not open!"; - error( RtAudioError::INVALID_USE ); - } -} - -void RtApi :: clearStreamInfo() -{ - stream_.mode = UNINITIALIZED; - stream_.state = STREAM_CLOSED; - stream_.sampleRate = 0; - stream_.bufferSize = 0; - stream_.nBuffers = 0; - stream_.userFormat = 0; - stream_.userInterleaved = true; - stream_.streamTime = 0.0; - stream_.apiHandle = 0; - stream_.deviceBuffer = 0; - stream_.callbackInfo.callback = 0; - stream_.callbackInfo.userData = 0; - stream_.callbackInfo.isRunning = false; - stream_.callbackInfo.errorCallback = 0; - for ( int i=0; i<2; i++ ) { - stream_.device[i] = 11111; - stream_.doConvertBuffer[i] = false; - stream_.deviceInterleaved[i] = true; - stream_.doByteSwap[i] = false; - stream_.nUserChannels[i] = 0; - stream_.nDeviceChannels[i] = 0; - stream_.channelOffset[i] = 0; - stream_.deviceFormat[i] = 0; - stream_.latency[i] = 0; - stream_.userBuffer[i] = 0; - stream_.convertInfo[i].channels = 0; - stream_.convertInfo[i].inJump = 0; - stream_.convertInfo[i].outJump = 0; - stream_.convertInfo[i].inFormat = 0; - stream_.convertInfo[i].outFormat = 0; - stream_.convertInfo[i].inOffset.clear(); - stream_.convertInfo[i].outOffset.clear(); - } -} - -unsigned int RtApi :: formatBytes( RtAudioFormat format ) -{ - if ( format == RTAUDIO_SINT16 ) - return 2; - else if ( format == RTAUDIO_SINT32 || format == RTAUDIO_FLOAT32 ) - return 4; - else if ( format == RTAUDIO_FLOAT64 ) - return 8; - else if ( format == RTAUDIO_SINT24 ) - return 3; - else if ( format == RTAUDIO_SINT8 ) - return 1; - - errorText_ = "RtApi::formatBytes: undefined format."; - error( RtAudioError::WARNING ); - - return 0; -} - -void RtApi :: setConvertInfo( StreamMode mode, unsigned int firstChannel ) -{ - if ( mode == INPUT ) { // convert device to user buffer - stream_.convertInfo[mode].inJump = stream_.nDeviceChannels[1]; - stream_.convertInfo[mode].outJump = stream_.nUserChannels[1]; - stream_.convertInfo[mode].inFormat = stream_.deviceFormat[1]; - stream_.convertInfo[mode].outFormat = stream_.userFormat; - } - else { // convert user to device buffer - stream_.convertInfo[mode].inJump = stream_.nUserChannels[0]; - stream_.convertInfo[mode].outJump = stream_.nDeviceChannels[0]; - stream_.convertInfo[mode].inFormat = stream_.userFormat; - stream_.convertInfo[mode].outFormat = stream_.deviceFormat[0]; - } - - if ( stream_.convertInfo[mode].inJump < stream_.convertInfo[mode].outJump ) - stream_.convertInfo[mode].channels = stream_.convertInfo[mode].inJump; - else - stream_.convertInfo[mode].channels = stream_.convertInfo[mode].outJump; - - // Set up the interleave/deinterleave offsets. - if ( stream_.deviceInterleaved[mode] != stream_.userInterleaved ) { - if ( ( mode == OUTPUT && stream_.deviceInterleaved[mode] ) || - ( mode == INPUT && stream_.userInterleaved ) ) { - for ( int k=0; k 0 ) { - if ( stream_.deviceInterleaved[mode] ) { - if ( mode == OUTPUT ) { - for ( int k=0; k> 8); - //out[info.outOffset[j]] >>= 8; - } - in += info.inJump; - out += info.outJump; - } - } - else if (info.inFormat == RTAUDIO_FLOAT32) { - Float32 *in = (Float32 *)inBuffer; - for (unsigned int i=0; i> 8); - } - in += info.inJump; - out += info.outJump; - } - } - else if (info.inFormat == RTAUDIO_SINT32) { - Int32 *in = (Int32 *)inBuffer; - for (unsigned int i=0; i> 16) & 0x0000ffff); - } - in += info.inJump; - out += info.outJump; - } - } - else if (info.inFormat == RTAUDIO_FLOAT32) { - Float32 *in = (Float32 *)inBuffer; - for (unsigned int i=0; i> 8) & 0x00ff); - } - in += info.inJump; - out += info.outJump; - } - } - else if (info.inFormat == RTAUDIO_SINT24) { - Int24 *in = (Int24 *)inBuffer; - for (unsigned int i=0; i> 16); - } - in += info.inJump; - out += info.outJump; - } - } - else if (info.inFormat == RTAUDIO_SINT32) { - Int32 *in = (Int32 *)inBuffer; - for (unsigned int i=0; i> 24) & 0x000000ff); - } - in += info.inJump; - out += info.outJump; - } - } - else if (info.inFormat == RTAUDIO_FLOAT32) { - Float32 *in = (Float32 *)inBuffer; - for (unsigned int i=0; i>8) | (x<<8); } -//static inline uint32_t bswap_32(uint32_t x) { return (bswap_16(x&0xffff)<<16) | (bswap_16(x>>16)); } -//static inline uint64_t bswap_64(uint64_t x) { return (((unsigned long long)bswap_32(x&0xffffffffull))<<32) | (bswap_32(x>>32)); } - -void RtApi :: byteSwapBuffer( char *buffer, unsigned int samples, RtAudioFormat format ) -{ - char val; - char *ptr; - - ptr = buffer; - if ( format == RTAUDIO_SINT16 ) { - for ( unsigned int i=0; i= 4 - #define RTAUDIO_DLL_PUBLIC __attribute__( (visibility( "default" )) ) - #else - #define RTAUDIO_DLL_PUBLIC - #endif -#endif - -#include -#include -#include -#include - -/*! \typedef typedef unsigned long RtAudioFormat; - \brief RtAudio data format type. - - Support for signed integers and floats. Audio data fed to/from an - RtAudio stream is assumed to ALWAYS be in host byte order. The - internal routines will automatically take care of any necessary - byte-swapping between the host format and the soundcard. Thus, - endian-ness is not a concern in the following format definitions. - - - \e RTAUDIO_SINT8: 8-bit signed integer. - - \e RTAUDIO_SINT16: 16-bit signed integer. - - \e RTAUDIO_SINT24: 24-bit signed integer. - - \e RTAUDIO_SINT32: 32-bit signed integer. - - \e RTAUDIO_FLOAT32: Normalized between plus/minus 1.0. - - \e RTAUDIO_FLOAT64: Normalized between plus/minus 1.0. -*/ -typedef unsigned long RtAudioFormat; -static const RtAudioFormat RTAUDIO_SINT8 = 0x1; // 8-bit signed integer. -static const RtAudioFormat RTAUDIO_SINT16 = 0x2; // 16-bit signed integer. -static const RtAudioFormat RTAUDIO_SINT24 = 0x4; // 24-bit signed integer. -static const RtAudioFormat RTAUDIO_SINT32 = 0x8; // 32-bit signed integer. -static const RtAudioFormat RTAUDIO_FLOAT32 = 0x10; // Normalized between plus/minus 1.0. -static const RtAudioFormat RTAUDIO_FLOAT64 = 0x20; // Normalized between plus/minus 1.0. - -/*! \typedef typedef unsigned long RtAudioStreamFlags; - \brief RtAudio stream option flags. - - The following flags can be OR'ed together to allow a client to - make changes to the default stream behavior: - - - \e RTAUDIO_NONINTERLEAVED: Use non-interleaved buffers (default = interleaved). - - \e RTAUDIO_MINIMIZE_LATENCY: Attempt to set stream parameters for lowest possible latency. - - \e RTAUDIO_HOG_DEVICE: Attempt grab device for exclusive use. - - \e RTAUDIO_ALSA_USE_DEFAULT: Use the "default" PCM device (ALSA only). - - \e RTAUDIO_JACK_DONT_CONNECT: Do not automatically connect ports (JACK only). - - By default, RtAudio streams pass and receive audio data from the - client in an interleaved format. By passing the - RTAUDIO_NONINTERLEAVED flag to the openStream() function, audio - data will instead be presented in non-interleaved buffers. In - this case, each buffer argument in the RtAudioCallback function - will point to a single array of data, with \c nFrames samples for - each channel concatenated back-to-back. For example, the first - sample of data for the second channel would be located at index \c - nFrames (assuming the \c buffer pointer was recast to the correct - data type for the stream). - - Certain audio APIs offer a number of parameters that influence the - I/O latency of a stream. By default, RtAudio will attempt to set - these parameters internally for robust (glitch-free) performance - (though some APIs, like Windows DirectSound, make this difficult). - By passing the RTAUDIO_MINIMIZE_LATENCY flag to the openStream() - function, internal stream settings will be influenced in an attempt - to minimize stream latency, though possibly at the expense of stream - performance. - - If the RTAUDIO_HOG_DEVICE flag is set, RtAudio will attempt to - open the input and/or output stream device(s) for exclusive use. - Note that this is not possible with all supported audio APIs. - - If the RTAUDIO_SCHEDULE_REALTIME flag is set, RtAudio will attempt - to select realtime scheduling (round-robin) for the callback thread. - - If the RTAUDIO_ALSA_USE_DEFAULT flag is set, RtAudio will attempt to - open the "default" PCM device when using the ALSA API. Note that this - will override any specified input or output device id. - - If the RTAUDIO_JACK_DONT_CONNECT flag is set, RtAudio will not attempt - to automatically connect the ports of the client to the audio device. -*/ -typedef unsigned int RtAudioStreamFlags; -static const RtAudioStreamFlags RTAUDIO_NONINTERLEAVED = 0x1; // Use non-interleaved buffers (default = interleaved). -static const RtAudioStreamFlags RTAUDIO_MINIMIZE_LATENCY = 0x2; // Attempt to set stream parameters for lowest possible latency. -static const RtAudioStreamFlags RTAUDIO_HOG_DEVICE = 0x4; // Attempt grab device and prevent use by others. -static const RtAudioStreamFlags RTAUDIO_SCHEDULE_REALTIME = 0x8; // Try to select realtime scheduling for callback thread. -static const RtAudioStreamFlags RTAUDIO_ALSA_USE_DEFAULT = 0x10; // Use the "default" PCM device (ALSA only). -static const RtAudioStreamFlags RTAUDIO_JACK_DONT_CONNECT = 0x20; // Do not automatically connect ports (JACK only). - -/*! \typedef typedef unsigned long RtAudioStreamStatus; - \brief RtAudio stream status (over- or underflow) flags. - - Notification of a stream over- or underflow is indicated by a - non-zero stream \c status argument in the RtAudioCallback function. - The stream status can be one of the following two options, - depending on whether the stream is open for output and/or input: - - - \e RTAUDIO_INPUT_OVERFLOW: Input data was discarded because of an overflow condition at the driver. - - \e RTAUDIO_OUTPUT_UNDERFLOW: The output buffer ran low, likely producing a break in the output sound. -*/ -typedef unsigned int RtAudioStreamStatus; -static const RtAudioStreamStatus RTAUDIO_INPUT_OVERFLOW = 0x1; // Input data was discarded because of an overflow condition at the driver. -static const RtAudioStreamStatus RTAUDIO_OUTPUT_UNDERFLOW = 0x2; // The output buffer ran low, likely causing a gap in the output sound. - -//! RtAudio callback function prototype. -/*! - All RtAudio clients must create a function of type RtAudioCallback - to read and/or write data from/to the audio stream. When the - underlying audio system is ready for new input or output data, this - function will be invoked. - - \param outputBuffer For output (or duplex) streams, the client - should write \c nFrames of audio sample frames into this - buffer. This argument should be recast to the datatype - specified when the stream was opened. For input-only - streams, this argument will be NULL. - - \param inputBuffer For input (or duplex) streams, this buffer will - hold \c nFrames of input audio sample frames. This - argument should be recast to the datatype specified when the - stream was opened. For output-only streams, this argument - will be NULL. - - \param nFrames The number of sample frames of input or output - data in the buffers. The actual buffer size in bytes is - dependent on the data type and number of channels in use. - - \param streamTime The number of seconds that have elapsed since the - stream was started. - - \param status If non-zero, this argument indicates a data overflow - or underflow condition for the stream. The particular - condition can be determined by comparison with the - RtAudioStreamStatus flags. - - \param userData A pointer to optional data provided by the client - when opening the stream (default = NULL). - - \return - To continue normal stream operation, the RtAudioCallback function - should return a value of zero. To stop the stream and drain the - output buffer, the function should return a value of one. To abort - the stream immediately, the client should return a value of two. - */ -typedef int (*RtAudioCallback)( void *outputBuffer, void *inputBuffer, - unsigned int nFrames, - double streamTime, - RtAudioStreamStatus status, - void *userData ); - -/************************************************************************/ -/*! \class RtAudioError - \brief Exception handling class for RtAudio. - - The RtAudioError class is quite simple but it does allow errors to be - "caught" by RtAudioError::Type. See the RtAudio documentation to know - which methods can throw an RtAudioError. -*/ -/************************************************************************/ - -class RTAUDIO_DLL_PUBLIC RtAudioError : public std::runtime_error -{ - public: - //! Defined RtAudioError types. - enum Type { - WARNING, /*!< A non-critical error. */ - DEBUG_WARNING, /*!< A non-critical error which might be useful for debugging. */ - UNSPECIFIED, /*!< The default, unspecified error type. */ - NO_DEVICES_FOUND, /*!< No devices found on system. */ - INVALID_DEVICE, /*!< An invalid device ID was specified. */ - MEMORY_ERROR, /*!< An error occured during memory allocation. */ - INVALID_PARAMETER, /*!< An invalid parameter was specified to a function. */ - INVALID_USE, /*!< The function was called incorrectly. */ - DRIVER_ERROR, /*!< A system driver error occured. */ - SYSTEM_ERROR, /*!< A system error occured. */ - THREAD_ERROR /*!< A thread error occured. */ - }; - - //! The constructor. - RtAudioError( const std::string& message, - Type type = RtAudioError::UNSPECIFIED ) - : std::runtime_error(message), type_(type) {} - - //! Prints thrown error message to stderr. - virtual void printMessage( void ) const - { std::cerr << '\n' << what() << "\n\n"; } - - //! Returns the thrown error message type. - virtual const Type& getType(void) const { return type_; } - - //! Returns the thrown error message string. - virtual const std::string getMessage(void) const - { return std::string(what()); } - - protected: - Type type_; -}; - -//! RtAudio error callback function prototype. -/*! - \param type Type of error. - \param errorText Error description. - */ -typedef void (*RtAudioErrorCallback)( RtAudioError::Type type, const std::string &errorText ); - -// **************************************************************** // -// -// RtAudio class declaration. -// -// RtAudio is a "controller" used to select an available audio i/o -// interface. It presents a common API for the user to call but all -// functionality is implemented by the class RtApi and its -// subclasses. RtAudio creates an instance of an RtApi subclass -// based on the user's API choice. If no choice is made, RtAudio -// attempts to make a "logical" API selection. -// -// **************************************************************** // - -class RtApi; - -class RTAUDIO_DLL_PUBLIC RtAudio -{ - public: - - //! Audio API specifier arguments. - enum Api { - UNSPECIFIED, /*!< Search for a working compiled API. */ - LINUX_ALSA, /*!< The Advanced Linux Sound Architecture API. */ - LINUX_PULSE, /*!< The Linux PulseAudio API. */ - LINUX_OSS, /*!< The Linux Open Sound System API. */ - UNIX_JACK, /*!< The Jack Low-Latency Audio Server API. */ - MACOSX_CORE, /*!< Macintosh OS-X Core Audio API. */ - WINDOWS_WASAPI, /*!< The Microsoft WASAPI API. */ - WINDOWS_ASIO, /*!< The Steinberg Audio Stream I/O API. */ - WINDOWS_DS, /*!< The Microsoft DirectSound API. */ - RTAUDIO_DUMMY, /*!< A compilable but non-functional API. */ - NUM_APIS /*!< Number of values in this enum. */ - }; - - //! The public device information structure for returning queried values. - struct DeviceInfo { - bool probed; /*!< true if the device capabilities were successfully probed. */ - std::string name; /*!< Character string device identifier. */ - unsigned int outputChannels; /*!< Maximum output channels supported by device. */ - unsigned int inputChannels; /*!< Maximum input channels supported by device. */ - unsigned int duplexChannels; /*!< Maximum simultaneous input/output channels supported by device. */ - bool isDefaultOutput; /*!< true if this is the default output device. */ - bool isDefaultInput; /*!< true if this is the default input device. */ - std::vector sampleRates; /*!< Supported sample rates (queried from list of standard rates). */ - unsigned int preferredSampleRate; /*!< Preferred sample rate, e.g. for WASAPI the system sample rate. */ - RtAudioFormat nativeFormats; /*!< Bit mask of supported data formats. */ - - // Default constructor. - DeviceInfo() - :probed(false), outputChannels(0), inputChannels(0), duplexChannels(0), - isDefaultOutput(false), isDefaultInput(false), preferredSampleRate(0), nativeFormats(0) {} - }; - - //! The structure for specifying input or ouput stream parameters. - struct StreamParameters { - unsigned int deviceId; /*!< Device index (0 to getDeviceCount() - 1). */ - unsigned int nChannels; /*!< Number of channels. */ - unsigned int firstChannel; /*!< First channel index on device (default = 0). */ - - // Default constructor. - StreamParameters() - : deviceId(0), nChannels(0), firstChannel(0) {} - }; - - //! The structure for specifying stream options. - /*! - The following flags can be OR'ed together to allow a client to - make changes to the default stream behavior: - - - \e RTAUDIO_NONINTERLEAVED: Use non-interleaved buffers (default = interleaved). - - \e RTAUDIO_MINIMIZE_LATENCY: Attempt to set stream parameters for lowest possible latency. - - \e RTAUDIO_HOG_DEVICE: Attempt grab device for exclusive use. - - \e RTAUDIO_SCHEDULE_REALTIME: Attempt to select realtime scheduling for callback thread. - - \e RTAUDIO_ALSA_USE_DEFAULT: Use the "default" PCM device (ALSA only). - - By default, RtAudio streams pass and receive audio data from the - client in an interleaved format. By passing the - RTAUDIO_NONINTERLEAVED flag to the openStream() function, audio - data will instead be presented in non-interleaved buffers. In - this case, each buffer argument in the RtAudioCallback function - will point to a single array of data, with \c nFrames samples for - each channel concatenated back-to-back. For example, the first - sample of data for the second channel would be located at index \c - nFrames (assuming the \c buffer pointer was recast to the correct - data type for the stream). - - Certain audio APIs offer a number of parameters that influence the - I/O latency of a stream. By default, RtAudio will attempt to set - these parameters internally for robust (glitch-free) performance - (though some APIs, like Windows DirectSound, make this difficult). - By passing the RTAUDIO_MINIMIZE_LATENCY flag to the openStream() - function, internal stream settings will be influenced in an attempt - to minimize stream latency, though possibly at the expense of stream - performance. - - If the RTAUDIO_HOG_DEVICE flag is set, RtAudio will attempt to - open the input and/or output stream device(s) for exclusive use. - Note that this is not possible with all supported audio APIs. - - If the RTAUDIO_SCHEDULE_REALTIME flag is set, RtAudio will attempt - to select realtime scheduling (round-robin) for the callback thread. - The \c priority parameter will only be used if the RTAUDIO_SCHEDULE_REALTIME - flag is set. It defines the thread's realtime priority. - - If the RTAUDIO_ALSA_USE_DEFAULT flag is set, RtAudio will attempt to - open the "default" PCM device when using the ALSA API. Note that this - will override any specified input or output device id. - - The \c numberOfBuffers parameter can be used to control stream - latency in the Windows DirectSound, Linux OSS, and Linux Alsa APIs - only. A value of two is usually the smallest allowed. Larger - numbers can potentially result in more robust stream performance, - though likely at the cost of stream latency. The value set by the - user is replaced during execution of the RtAudio::openStream() - function by the value actually used by the system. - - The \c streamName parameter can be used to set the client name - when using the Jack API. By default, the client name is set to - RtApiJack. However, if you wish to create multiple instances of - RtAudio with Jack, each instance must have a unique client name. - */ - struct StreamOptions { - RtAudioStreamFlags flags; /*!< A bit-mask of stream flags (RTAUDIO_NONINTERLEAVED, RTAUDIO_MINIMIZE_LATENCY, RTAUDIO_HOG_DEVICE, RTAUDIO_ALSA_USE_DEFAULT). */ - unsigned int numberOfBuffers; /*!< Number of stream buffers. */ - std::string streamName; /*!< A stream name (currently used only in Jack). */ - int priority; /*!< Scheduling priority of callback thread (only used with flag RTAUDIO_SCHEDULE_REALTIME). */ - - // Default constructor. - StreamOptions() - : flags(0), numberOfBuffers(0), priority(0) {} - }; - - //! A static function to determine the current RtAudio version. - static std::string getVersion( void ); - - //! A static function to determine the available compiled audio APIs. - /*! - The values returned in the std::vector can be compared against - the enumerated list values. Note that there can be more than one - API compiled for certain operating systems. - */ - static void getCompiledApi( std::vector &apis ); - - //! Return the name of a specified compiled audio API. - /*! - This obtains a short lower-case name used for identification purposes. - This value is guaranteed to remain identical across library versions. - If the API is unknown, this function will return the empty string. - */ - static std::string getApiName( RtAudio::Api api ); - - //! Return the display name of a specified compiled audio API. - /*! - This obtains a long name used for display purposes. - If the API is unknown, this function will return the empty string. - */ - static std::string getApiDisplayName( RtAudio::Api api ); - - //! Return the compiled audio API having the given name. - /*! - A case insensitive comparison will check the specified name - against the list of compiled APIs, and return the one which - matches. On failure, the function returns UNSPECIFIED. - */ - static RtAudio::Api getCompiledApiByName( const std::string &name ); - - //! The class constructor. - /*! - The constructor performs minor initialization tasks. An exception - can be thrown if no API support is compiled. - - If no API argument is specified and multiple API support has been - compiled, the default order of use is JACK, ALSA, OSS (Linux - systems) and ASIO, DS (Windows systems). - */ - RtAudio( RtAudio::Api api=UNSPECIFIED ); - - //! The destructor. - /*! - If a stream is running or open, it will be stopped and closed - automatically. - */ - ~RtAudio(); - - //! Returns the audio API specifier for the current instance of RtAudio. - RtAudio::Api getCurrentApi( void ); - - //! A public function that queries for the number of audio devices available. - /*! - This function performs a system query of available devices each time it - is called, thus supporting devices connected \e after instantiation. If - a system error occurs during processing, a warning will be issued. - */ - unsigned int getDeviceCount( void ); - - //! Return an RtAudio::DeviceInfo structure for a specified device number. - /*! - - Any device integer between 0 and getDeviceCount() - 1 is valid. - If an invalid argument is provided, an RtAudioError (type = INVALID_USE) - will be thrown. If a device is busy or otherwise unavailable, the - structure member "probed" will have a value of "false" and all - other members are undefined. If the specified device is the - current default input or output device, the corresponding - "isDefault" member will have a value of "true". - */ - RtAudio::DeviceInfo getDeviceInfo( unsigned int device ); - - //! A function that returns the index of the default output device. - /*! - If the underlying audio API does not provide a "default - device", or if no devices are available, the return value will be - 0. Note that this is a valid device identifier and it is the - client's responsibility to verify that a device is available - before attempting to open a stream. - */ - unsigned int getDefaultOutputDevice( void ); - - //! A function that returns the index of the default input device. - /*! - If the underlying audio API does not provide a "default - device", or if no devices are available, the return value will be - 0. Note that this is a valid device identifier and it is the - client's responsibility to verify that a device is available - before attempting to open a stream. - */ - unsigned int getDefaultInputDevice( void ); - - //! A public function for opening a stream with the specified parameters. - /*! - An RtAudioError (type = SYSTEM_ERROR) is thrown if a stream cannot be - opened with the specified parameters or an error occurs during - processing. An RtAudioError (type = INVALID_USE) is thrown if any - invalid device ID or channel number parameters are specified. - - \param outputParameters Specifies output stream parameters to use - when opening a stream, including a device ID, number of channels, - and starting channel number. For input-only streams, this - argument should be NULL. The device ID is an index value between - 0 and getDeviceCount() - 1. - \param inputParameters Specifies input stream parameters to use - when opening a stream, including a device ID, number of channels, - and starting channel number. For output-only streams, this - argument should be NULL. The device ID is an index value between - 0 and getDeviceCount() - 1. - \param format An RtAudioFormat specifying the desired sample data format. - \param sampleRate The desired sample rate (sample frames per second). - \param *bufferFrames A pointer to a value indicating the desired - internal buffer size in sample frames. The actual value - used by the device is returned via the same pointer. A - value of zero can be specified, in which case the lowest - allowable value is determined. - \param callback A client-defined function that will be invoked - when input data is available and/or output data is needed. - \param userData An optional pointer to data that can be accessed - from within the callback function. - \param options An optional pointer to a structure containing various - global stream options, including a list of OR'ed RtAudioStreamFlags - and a suggested number of stream buffers that can be used to - control stream latency. More buffers typically result in more - robust performance, though at a cost of greater latency. If a - value of zero is specified, a system-specific median value is - chosen. If the RTAUDIO_MINIMIZE_LATENCY flag bit is set, the - lowest allowable value is used. The actual value used is - returned via the structure argument. The parameter is API dependent. - \param errorCallback A client-defined function that will be invoked - when an error has occured. - */ - void openStream( RtAudio::StreamParameters *outputParameters, - RtAudio::StreamParameters *inputParameters, - RtAudioFormat format, unsigned int sampleRate, - unsigned int *bufferFrames, RtAudioCallback callback, - void *userData = NULL, RtAudio::StreamOptions *options = NULL, RtAudioErrorCallback errorCallback = NULL ); - - //! A function that closes a stream and frees any associated stream memory. - /*! - If a stream is not open, this function issues a warning and - returns (no exception is thrown). - */ - void closeStream( void ); - - //! A function that starts a stream. - /*! - An RtAudioError (type = SYSTEM_ERROR) is thrown if an error occurs - during processing. An RtAudioError (type = INVALID_USE) is thrown if a - stream is not open. A warning is issued if the stream is already - running. - */ - void startStream( void ); - - //! Stop a stream, allowing any samples remaining in the output queue to be played. - /*! - An RtAudioError (type = SYSTEM_ERROR) is thrown if an error occurs - during processing. An RtAudioError (type = INVALID_USE) is thrown if a - stream is not open. A warning is issued if the stream is already - stopped. - */ - void stopStream( void ); - - //! Stop a stream, discarding any samples remaining in the input/output queue. - /*! - An RtAudioError (type = SYSTEM_ERROR) is thrown if an error occurs - during processing. An RtAudioError (type = INVALID_USE) is thrown if a - stream is not open. A warning is issued if the stream is already - stopped. - */ - void abortStream( void ); - - //! Returns true if a stream is open and false if not. - bool isStreamOpen( void ) const; - - //! Returns true if the stream is running and false if it is stopped or not open. - bool isStreamRunning( void ) const; - - //! Returns the number of elapsed seconds since the stream was started. - /*! - If a stream is not open, an RtAudioError (type = INVALID_USE) will be thrown. - */ - double getStreamTime( void ); - - //! Set the stream time to a time in seconds greater than or equal to 0.0. - /*! - If a stream is not open, an RtAudioError (type = INVALID_USE) will be thrown. - */ - void setStreamTime( double time ); - - //! Returns the internal stream latency in sample frames. - /*! - The stream latency refers to delay in audio input and/or output - caused by internal buffering by the audio system and/or hardware. - For duplex streams, the returned value will represent the sum of - the input and output latencies. If a stream is not open, an - RtAudioError (type = INVALID_USE) will be thrown. If the API does not - report latency, the return value will be zero. - */ - long getStreamLatency( void ); - - //! Returns actual sample rate in use by the stream. - /*! - On some systems, the sample rate used may be slightly different - than that specified in the stream parameters. If a stream is not - open, an RtAudioError (type = INVALID_USE) will be thrown. - */ - unsigned int getStreamSampleRate( void ); - - //! Specify whether warning messages should be printed to stderr. - void showWarnings( bool value = true ); - - protected: - - void openRtApi( RtAudio::Api api ); - RtApi *rtapi_; -}; - -// Operating system dependent thread functionality. -#if defined(__WINDOWS_DS__) || defined(__WINDOWS_ASIO__) || defined(__WINDOWS_WASAPI__) - - #ifndef NOMINMAX - #define NOMINMAX - #endif - #include - #include - #include - - typedef uintptr_t ThreadHandle; - typedef CRITICAL_SECTION StreamMutex; - -#elif defined(__LINUX_ALSA__) || defined(__LINUX_PULSE__) || defined(__UNIX_JACK__) || defined(__LINUX_OSS__) || defined(__MACOSX_CORE__) - // Using pthread library for various flavors of unix. - #include - - typedef pthread_t ThreadHandle; - typedef pthread_mutex_t StreamMutex; - -#else // Setup for "dummy" behavior - - #define __RTAUDIO_DUMMY__ - typedef int ThreadHandle; - typedef int StreamMutex; - -#endif - -// This global structure type is used to pass callback information -// between the private RtAudio stream structure and global callback -// handling functions. -struct CallbackInfo { - void *object; // Used as a "this" pointer. - ThreadHandle thread; - void *callback; - void *userData; - void *errorCallback; - void *apiInfo; // void pointer for API specific callback information - bool isRunning; - bool doRealtime; - int priority; - - // Default constructor. - CallbackInfo() - :object(0), callback(0), userData(0), errorCallback(0), apiInfo(0), isRunning(false), doRealtime(false), priority(0) {} -}; - -// **************************************************************** // -// -// RtApi class declaration. -// -// Subclasses of RtApi contain all API- and OS-specific code necessary -// to fully implement the RtAudio API. -// -// Note that RtApi is an abstract base class and cannot be -// explicitly instantiated. The class RtAudio will create an -// instance of an RtApi subclass (RtApiOss, RtApiAlsa, -// RtApiJack, RtApiCore, RtApiDs, or RtApiAsio). -// -// **************************************************************** // - -#pragma pack(push, 1) -class S24 { - - protected: - unsigned char c3[3]; - - public: - S24() {} - - S24& operator = ( const int& i ) { - c3[0] = (i & 0x000000ff); - c3[1] = (i & 0x0000ff00) >> 8; - c3[2] = (i & 0x00ff0000) >> 16; - return *this; - } - - S24( const double& d ) { *this = (int) d; } - S24( const float& f ) { *this = (int) f; } - S24( const signed short& s ) { *this = (int) s; } - S24( const char& c ) { *this = (int) c; } - - int asInt() { - int i = c3[0] | (c3[1] << 8) | (c3[2] << 16); - if (i & 0x800000) i |= ~0xffffff; - return i; - } -}; -#pragma pack(pop) - -#if defined( HAVE_GETTIMEOFDAY ) - #include -#endif - -#include - -class RTAUDIO_DLL_PUBLIC RtApi -{ -public: - - RtApi(); - virtual ~RtApi(); - virtual RtAudio::Api getCurrentApi( void ) = 0; - virtual unsigned int getDeviceCount( void ) = 0; - virtual RtAudio::DeviceInfo getDeviceInfo( unsigned int device ) = 0; - virtual unsigned int getDefaultInputDevice( void ); - virtual unsigned int getDefaultOutputDevice( void ); - void openStream( RtAudio::StreamParameters *outputParameters, - RtAudio::StreamParameters *inputParameters, - RtAudioFormat format, unsigned int sampleRate, - unsigned int *bufferFrames, RtAudioCallback callback, - void *userData, RtAudio::StreamOptions *options, - RtAudioErrorCallback errorCallback ); - virtual void closeStream( void ); - virtual void startStream( void ) = 0; - virtual void stopStream( void ) = 0; - virtual void abortStream( void ) = 0; - long getStreamLatency( void ); - unsigned int getStreamSampleRate( void ); - virtual double getStreamTime( void ); - virtual void setStreamTime( double time ); - bool isStreamOpen( void ) const { return stream_.state != STREAM_CLOSED; } - bool isStreamRunning( void ) const { return stream_.state == STREAM_RUNNING; } - void showWarnings( bool value ) { showWarnings_ = value; } - - -protected: - - static const unsigned int MAX_SAMPLE_RATES; - static const unsigned int SAMPLE_RATES[]; - - enum { FAILURE, SUCCESS }; - - enum StreamState { - STREAM_STOPPED, - STREAM_STOPPING, - STREAM_RUNNING, - STREAM_CLOSED = -50 - }; - - enum StreamMode { - OUTPUT, - INPUT, - DUPLEX, - UNINITIALIZED = -75 - }; - - // A protected structure used for buffer conversion. - struct ConvertInfo { - int channels; - int inJump, outJump; - RtAudioFormat inFormat, outFormat; - std::vector inOffset; - std::vector outOffset; - }; - - // A protected structure for audio streams. - struct RtApiStream { - unsigned int device[2]; // Playback and record, respectively. - void *apiHandle; // void pointer for API specific stream handle information - StreamMode mode; // OUTPUT, INPUT, or DUPLEX. - StreamState state; // STOPPED, RUNNING, or CLOSED - char *userBuffer[2]; // Playback and record, respectively. - char *deviceBuffer; - bool doConvertBuffer[2]; // Playback and record, respectively. - bool userInterleaved; - bool deviceInterleaved[2]; // Playback and record, respectively. - bool doByteSwap[2]; // Playback and record, respectively. - unsigned int sampleRate; - unsigned int bufferSize; - unsigned int nBuffers; - unsigned int nUserChannels[2]; // Playback and record, respectively. - unsigned int nDeviceChannels[2]; // Playback and record channels, respectively. - unsigned int channelOffset[2]; // Playback and record, respectively. - unsigned long latency[2]; // Playback and record, respectively. - RtAudioFormat userFormat; - RtAudioFormat deviceFormat[2]; // Playback and record, respectively. - StreamMutex mutex; - CallbackInfo callbackInfo; - ConvertInfo convertInfo[2]; - double streamTime; // Number of elapsed seconds since the stream started. - -#if defined(HAVE_GETTIMEOFDAY) - struct timeval lastTickTimestamp; -#endif - - RtApiStream() - :apiHandle(0), deviceBuffer(0) { device[0] = 11111; device[1] = 11111; } - }; - - typedef S24 Int24; - typedef signed short Int16; - typedef signed int Int32; - typedef float Float32; - typedef double Float64; - - std::ostringstream errorStream_; - std::string errorText_; - bool showWarnings_; - RtApiStream stream_; - bool firstErrorOccurred_; - - /*! - Protected, api-specific method that attempts to open a device - with the given parameters. This function MUST be implemented by - all subclasses. If an error is encountered during the probe, a - "warning" message is reported and FAILURE is returned. A - successful probe is indicated by a return value of SUCCESS. - */ - virtual bool probeDeviceOpen( unsigned int device, StreamMode mode, unsigned int channels, - unsigned int firstChannel, unsigned int sampleRate, - RtAudioFormat format, unsigned int *bufferSize, - RtAudio::StreamOptions *options ); - - //! A protected function used to increment the stream time. - void tickStreamTime( void ); - - //! Protected common method to clear an RtApiStream structure. - void clearStreamInfo(); - - /*! - Protected common method that throws an RtAudioError (type = - INVALID_USE) if a stream is not open. - */ - void verifyStream( void ); - - //! Protected common error method to allow global control over error handling. - void error( RtAudioError::Type type ); - - /*! - Protected method used to perform format, channel number, and/or interleaving - conversions between the user and device buffers. - */ - void convertBuffer( char *outBuffer, char *inBuffer, ConvertInfo &info ); - - //! Protected common method used to perform byte-swapping on buffers. - void byteSwapBuffer( char *buffer, unsigned int samples, RtAudioFormat format ); - - //! Protected common method that returns the number of bytes for a given format. - unsigned int formatBytes( RtAudioFormat format ); - - //! Protected common method that sets up the parameters for buffer conversion. - void setConvertInfo( StreamMode mode, unsigned int firstChannel ); -}; - -// **************************************************************** // -// -// Inline RtAudio definitions. -// -// **************************************************************** // - -inline RtAudio::Api RtAudio :: getCurrentApi( void ) { return rtapi_->getCurrentApi(); } -inline unsigned int RtAudio :: getDeviceCount( void ) { return rtapi_->getDeviceCount(); } -inline RtAudio::DeviceInfo RtAudio :: getDeviceInfo( unsigned int device ) { return rtapi_->getDeviceInfo( device ); } -inline unsigned int RtAudio :: getDefaultInputDevice( void ) { return rtapi_->getDefaultInputDevice(); } -inline unsigned int RtAudio :: getDefaultOutputDevice( void ) { return rtapi_->getDefaultOutputDevice(); } -inline void RtAudio :: closeStream( void ) { return rtapi_->closeStream(); } -inline void RtAudio :: startStream( void ) { return rtapi_->startStream(); } -inline void RtAudio :: stopStream( void ) { return rtapi_->stopStream(); } -inline void RtAudio :: abortStream( void ) { return rtapi_->abortStream(); } -inline bool RtAudio :: isStreamOpen( void ) const { return rtapi_->isStreamOpen(); } -inline bool RtAudio :: isStreamRunning( void ) const { return rtapi_->isStreamRunning(); } -inline long RtAudio :: getStreamLatency( void ) { return rtapi_->getStreamLatency(); } -inline unsigned int RtAudio :: getStreamSampleRate( void ) { return rtapi_->getStreamSampleRate(); } -inline double RtAudio :: getStreamTime( void ) { return rtapi_->getStreamTime(); } -inline void RtAudio :: setStreamTime( double time ) { return rtapi_->setStreamTime( time ); } -inline void RtAudio :: showWarnings( bool value ) { rtapi_->showWarnings( value ); } - -// RtApi Subclass prototypes. - -#if defined(__MACOSX_CORE__) - -#include - -class RtApiCore: public RtApi -{ -public: - - RtApiCore(); - ~RtApiCore(); - RtAudio::Api getCurrentApi( void ) { return RtAudio::MACOSX_CORE; } - unsigned int getDeviceCount( void ); - RtAudio::DeviceInfo getDeviceInfo( unsigned int device ); - unsigned int getDefaultOutputDevice( void ); - unsigned int getDefaultInputDevice( void ); - void closeStream( void ); - void startStream( void ); - void stopStream( void ); - void abortStream( void ); - - // This function is intended for internal use only. It must be - // public because it is called by the internal callback handler, - // which is not a member of RtAudio. External use of this function - // will most likely produce highly undesireable results! - bool callbackEvent( AudioDeviceID deviceId, - const AudioBufferList *inBufferList, - const AudioBufferList *outBufferList ); - - private: - - bool probeDeviceOpen( unsigned int device, StreamMode mode, unsigned int channels, - unsigned int firstChannel, unsigned int sampleRate, - RtAudioFormat format, unsigned int *bufferSize, - RtAudio::StreamOptions *options ); - static const char* getErrorCode( OSStatus code ); -}; - -#endif - -#if defined(__UNIX_JACK__) - -class RtApiJack: public RtApi -{ -public: - - RtApiJack(); - ~RtApiJack(); - RtAudio::Api getCurrentApi( void ) { return RtAudio::UNIX_JACK; } - unsigned int getDeviceCount( void ); - RtAudio::DeviceInfo getDeviceInfo( unsigned int device ); - void closeStream( void ); - void startStream( void ); - void stopStream( void ); - void abortStream( void ); - - // This function is intended for internal use only. It must be - // public because it is called by the internal callback handler, - // which is not a member of RtAudio. External use of this function - // will most likely produce highly undesireable results! - bool callbackEvent( unsigned long nframes ); - - private: - - bool probeDeviceOpen( unsigned int device, StreamMode mode, unsigned int channels, - unsigned int firstChannel, unsigned int sampleRate, - RtAudioFormat format, unsigned int *bufferSize, - RtAudio::StreamOptions *options ); - - bool shouldAutoconnect_; -}; - -#endif - -#if defined(__WINDOWS_ASIO__) - -class RtApiAsio: public RtApi -{ -public: - - RtApiAsio(); - ~RtApiAsio(); - RtAudio::Api getCurrentApi( void ) { return RtAudio::WINDOWS_ASIO; } - unsigned int getDeviceCount( void ); - RtAudio::DeviceInfo getDeviceInfo( unsigned int device ); - void closeStream( void ); - void startStream( void ); - void stopStream( void ); - void abortStream( void ); - - // This function is intended for internal use only. It must be - // public because it is called by the internal callback handler, - // which is not a member of RtAudio. External use of this function - // will most likely produce highly undesireable results! - bool callbackEvent( long bufferIndex ); - - private: - - std::vector devices_; - void saveDeviceInfo( void ); - bool coInitialized_; - bool probeDeviceOpen( unsigned int device, StreamMode mode, unsigned int channels, - unsigned int firstChannel, unsigned int sampleRate, - RtAudioFormat format, unsigned int *bufferSize, - RtAudio::StreamOptions *options ); -}; - -#endif - -#if defined(__WINDOWS_DS__) - -class RtApiDs: public RtApi -{ -public: - - RtApiDs(); - ~RtApiDs(); - RtAudio::Api getCurrentApi( void ) { return RtAudio::WINDOWS_DS; } - unsigned int getDeviceCount( void ); - unsigned int getDefaultOutputDevice( void ); - unsigned int getDefaultInputDevice( void ); - RtAudio::DeviceInfo getDeviceInfo( unsigned int device ); - void closeStream( void ); - void startStream( void ); - void stopStream( void ); - void abortStream( void ); - - // This function is intended for internal use only. It must be - // public because it is called by the internal callback handler, - // which is not a member of RtAudio. External use of this function - // will most likely produce highly undesireable results! - void callbackEvent( void ); - - private: - - bool coInitialized_; - bool buffersRolling; - long duplexPrerollBytes; - std::vector dsDevices; - bool probeDeviceOpen( unsigned int device, StreamMode mode, unsigned int channels, - unsigned int firstChannel, unsigned int sampleRate, - RtAudioFormat format, unsigned int *bufferSize, - RtAudio::StreamOptions *options ); -}; - -#endif - -#if defined(__WINDOWS_WASAPI__) - -struct IMMDeviceEnumerator; - -class RtApiWasapi : public RtApi -{ -public: - RtApiWasapi(); - virtual ~RtApiWasapi(); - - RtAudio::Api getCurrentApi( void ) { return RtAudio::WINDOWS_WASAPI; } - unsigned int getDeviceCount( void ); - RtAudio::DeviceInfo getDeviceInfo( unsigned int device ); - unsigned int getDefaultOutputDevice( void ); - unsigned int getDefaultInputDevice( void ); - void closeStream( void ); - void startStream( void ); - void stopStream( void ); - void abortStream( void ); - -private: - bool coInitialized_; - IMMDeviceEnumerator* deviceEnumerator_; - - bool probeDeviceOpen( unsigned int device, StreamMode mode, unsigned int channels, - unsigned int firstChannel, unsigned int sampleRate, - RtAudioFormat format, unsigned int* bufferSize, - RtAudio::StreamOptions* options ); - - static DWORD WINAPI runWasapiThread( void* wasapiPtr ); - static DWORD WINAPI stopWasapiThread( void* wasapiPtr ); - static DWORD WINAPI abortWasapiThread( void* wasapiPtr ); - void wasapiThread(); -}; - -#endif - -#if defined(__LINUX_ALSA__) - -class RtApiAlsa: public RtApi -{ -public: - - RtApiAlsa(); - ~RtApiAlsa(); - RtAudio::Api getCurrentApi() { return RtAudio::LINUX_ALSA; } - unsigned int getDeviceCount( void ); - RtAudio::DeviceInfo getDeviceInfo( unsigned int device ); - void closeStream( void ); - void startStream( void ); - void stopStream( void ); - void abortStream( void ); - - // This function is intended for internal use only. It must be - // public because it is called by the internal callback handler, - // which is not a member of RtAudio. External use of this function - // will most likely produce highly undesireable results! - void callbackEvent( void ); - - private: - - std::vector devices_; - void saveDeviceInfo( void ); - bool probeDeviceOpen( unsigned int device, StreamMode mode, unsigned int channels, - unsigned int firstChannel, unsigned int sampleRate, - RtAudioFormat format, unsigned int *bufferSize, - RtAudio::StreamOptions *options ); -}; - -#endif - -#if defined(__LINUX_PULSE__) - -class RtApiPulse: public RtApi -{ -public: - ~RtApiPulse(); - RtAudio::Api getCurrentApi() { return RtAudio::LINUX_PULSE; } - unsigned int getDeviceCount( void ); - RtAudio::DeviceInfo getDeviceInfo( unsigned int device ); - void closeStream( void ); - void startStream( void ); - void stopStream( void ); - void abortStream( void ); - - // This function is intended for internal use only. It must be - // public because it is called by the internal callback handler, - // which is not a member of RtAudio. External use of this function - // will most likely produce highly undesireable results! - void callbackEvent( void ); - - private: - - std::vector devices_; - void saveDeviceInfo( void ); - bool probeDeviceOpen( unsigned int device, StreamMode mode, unsigned int channels, - unsigned int firstChannel, unsigned int sampleRate, - RtAudioFormat format, unsigned int *bufferSize, - RtAudio::StreamOptions *options ); -}; - -#endif - -#if defined(__LINUX_OSS__) - -class RtApiOss: public RtApi -{ -public: - - RtApiOss(); - ~RtApiOss(); - RtAudio::Api getCurrentApi() { return RtAudio::LINUX_OSS; } - unsigned int getDeviceCount( void ); - RtAudio::DeviceInfo getDeviceInfo( unsigned int device ); - void closeStream( void ); - void startStream( void ); - void stopStream( void ); - void abortStream( void ); - - // This function is intended for internal use only. It must be - // public because it is called by the internal callback handler, - // which is not a member of RtAudio. External use of this function - // will most likely produce highly undesireable results! - void callbackEvent( void ); - - private: - - bool probeDeviceOpen( unsigned int device, StreamMode mode, unsigned int channels, - unsigned int firstChannel, unsigned int sampleRate, - RtAudioFormat format, unsigned int *bufferSize, - RtAudio::StreamOptions *options ); -}; - -#endif - -#if defined(__RTAUDIO_DUMMY__) - -class RtApiDummy: public RtApi -{ -public: - - RtApiDummy() { errorText_ = "RtApiDummy: This class provides no functionality."; error( RtAudioError::WARNING ); } - RtAudio::Api getCurrentApi( void ) { return RtAudio::RTAUDIO_DUMMY; } - unsigned int getDeviceCount( void ) { return 0; } - RtAudio::DeviceInfo getDeviceInfo( unsigned int /*device*/ ) { RtAudio::DeviceInfo info; return info; } - void closeStream( void ) {} - void startStream( void ) {} - void stopStream( void ) {} - void abortStream( void ) {} - - private: - - bool probeDeviceOpen( unsigned int /*device*/, StreamMode /*mode*/, unsigned int /*channels*/, - unsigned int /*firstChannel*/, unsigned int /*sampleRate*/, - RtAudioFormat /*format*/, unsigned int * /*bufferSize*/, - RtAudio::StreamOptions * /*options*/ ) { return false; } -}; - -#endif - -#endif - -// Indentation settings for Vim and Emacs -// -// Local Variables: -// c-basic-offset: 2 -// indent-tabs-mode: nil -// End: -// -// vim: et sts=2 sw=2 diff -Nru bambootracker-0.4.5+git20121209/BambooTracker/audio/RtAudio/RtAudio.pri bambootracker-0.4.6/BambooTracker/audio/RtAudio/RtAudio.pri --- bambootracker-0.4.5+git20121209/BambooTracker/audio/RtAudio/RtAudio.pri 2020-12-08 14:05:28.000000000 +0000 +++ bambootracker-0.4.6/BambooTracker/audio/RtAudio/RtAudio.pri 1970-01-01 00:00:00.000000000 +0000 @@ -1,54 +0,0 @@ -SOURCES += \ - $$PWD/RtAudio.cpp - -HEADERS += \ - $$PWD/RtAudio.hpp - -win32 { - DEFINES += __WINDOWS_DS__ - LIBS += -lole32 -lwinmm -ldsound -luser32 - - greaterThan(QT_MAJOR_VERSION, 4):greaterThan(QT_MINOR_VERSION, 5) { - DEFINES += __WINDOWS_WASAPI__ - LIBS += -lole32 -lwinmm -lksuser -lmfplat -lmfuuid -lwmcodecdspuuid - } -} -else:macx { - DEFINES += __MACOSX_CORE__ - LIBS += -framework CoreAudio -framework CoreFoundation -lpthread - - use_jack { - DEFINES += __UNIX_JACK__ - LIBS += -ljack - } -} -else:linux { - DEFINES += __LINUX_ALSA__ - LIBS += -lasound -lpthread - - use_pulse { - DEFINES += __LINUX_PULSE__ - LIBS += -lpulse-simple -lpulse - } - use_jack { - DEFINES += __UNIX_JACK__ - LIBS += -ljack - } -} -else:bsd { - DEFINES += __LINUX_OSS__ - LIBS += -lpthread - - use_alsa { - DEFINES += __LINUX_ALSA__ - LIBS += -lasound - } - use_pulse { - DEFINES += __LINUX_PULSE__ - LIBS += -lpulse-simple -lpulse - } - use_jack { - DEFINES += __UNIX_JACK__ - LIBS += -ljack - } -} diff -Nru bambootracker-0.4.5+git20121209/BambooTracker/bamboo_tracker.cpp bambootracker-0.4.6/BambooTracker/bamboo_tracker.cpp --- bambootracker-0.4.5+git20121209/BambooTracker/bamboo_tracker.cpp 2020-12-08 14:05:28.000000000 +0000 +++ bambootracker-0.4.6/BambooTracker/bamboo_tracker.cpp 2021-02-11 14:37:17.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (C) 2018-2020 Rerrah + * Copyright (C) 2018-2021 Rerrah * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation @@ -26,24 +26,34 @@ #include "bamboo_tracker.hpp" #include #include -#include #include #include +#include +#include "configuration.hpp" +#include "opna_controller.hpp" +#include "playback.hpp" +#include "tick_counter.hpp" #include "command/commands.hpp" #include "chip/register_write_logger.hpp" #include "io/module_io.hpp" #include "io/instrument_io.hpp" #include "io/bank_io.hpp" #include "bank.hpp" +#include "note.hpp" #include "song_length_calculator.hpp" +#include "utils.hpp" -const uint32_t BambooTracker::CHIP_CLOCK = 3993600 * 2; +namespace +{ +const uint32_t CHIP_CLOCK = 3993600 * 2; +} BambooTracker::BambooTracker(std::weak_ptr config) : instMan_(std::make_shared(config.lock()->getOverwriteUnusedUneditedPropety())), + jamMan_(std::make_unique()), tickCounter_(std::make_shared()), mod_(std::make_shared()), - curOctave_(4), + curOctave_(Note::DEFAULT_OCTAVE), curSongNum_(0), curTrackNum_(0), curOrderNum_(0), @@ -64,7 +74,6 @@ setMasterVolumeSSG(config.lock()->getMixerVolumeSSG()); songStyle_ = mod_->getSong(curSongNum_).getStyle(); - jamMan_ = std::make_unique(); playback_ = std::make_unique( opnaCtrl_, instMan_, tickCounter_, mod_, config.lock()->getRetrieveChannelState()); @@ -73,6 +82,8 @@ volFMReversed_ = config.lock()->getReverseFMVolumeOrder(); } +BambooTracker::~BambooTracker() = default; + /********** Change configuration **********/ void BambooTracker::changeConfiguration(std::weak_ptr config) { @@ -119,8 +130,7 @@ TrackAttribute BambooTracker::getCurrentTrackAttribute() const { - TrackAttribute ret = songStyle_.trackAttribs.at(static_cast(curTrackNum_)); - return ret; + return songStyle_.trackAttribs.at(static_cast(curTrackNum_)); } /********** Current instrument **********/ @@ -135,7 +145,7 @@ } /********** Instrument edit **********/ -void BambooTracker::addInstrument(int num, InstrumentType type, std::string name) +void BambooTracker::addInstrument(int num, InstrumentType type, const std::string& name) { comMan_.invoke(std::make_unique(instMan_, num, type, name)); } @@ -167,11 +177,10 @@ comMan_.invoke(std::make_unique(instMan_, mod_, a, b, curSongNum_, patternChange)); } -void BambooTracker::loadInstrument(io::BinaryContainer& container, std::string path, int instNum) +void BambooTracker::loadInstrument(io::BinaryContainer& container, const std::string& path, int instNum) { - auto inst = io::InstrumentIO::getInstance().loadInstrument(container, path, instMan_, instNum); - comMan_.invoke(std::make_unique( - instMan_, std::unique_ptr(inst))); + AbstractInstrument* inst = io::InstrumentIO::getInstance().loadInstrument(container, path, instMan_, instNum); + comMan_.invoke(std::make_unique(instMan_, std::unique_ptr(inst))); } void BambooTracker::saveInstrument(io::BinaryContainer& container, int instNum) @@ -181,12 +190,12 @@ void BambooTracker::importInstrument(const AbstractBank &bank, size_t index, int instNum) { - auto inst = bank.loadInstrument(index, instMan_, instNum); + AbstractInstrument* inst = bank.loadInstrument(index, instMan_, instNum); comMan_.invoke(std::make_unique( instMan_, std::unique_ptr(inst))); } -void BambooTracker::exportInstruments(io::BinaryContainer& container, std::vector instNums) +void BambooTracker::exportInstruments(io::BinaryContainer& container, const std::vector& instNums) { io::BankIO::getInstance().saveBank(container, instMan_, instNums); } @@ -196,7 +205,7 @@ return instMan_->findFirstFreeInstrument(); } -void BambooTracker::setInstrumentName(int num, std::string name) +void BambooTracker::setInstrumentName(int num, const std::string& name) { comMan_.invoke(std::make_unique(instMan_, num, name)); } @@ -213,11 +222,11 @@ std::vector BambooTracker::getUnusedInstrumentIndices() const { + std::vector instIdcs = instMan_->getInstrumentIndices(); + std::set regdInsts = mod_->getRegisterdInstruments(); + std::vector unused; - std::unordered_set regdInsts = mod_->getRegisterdInstruments(); - for (auto& inst : instMan_->getInstrumentIndices()) { - if (!regdInsts.count(inst)) unused.push_back(inst); - } + std::set_difference(instIdcs.begin(), instIdcs.end(), regdInsts.begin(), regdInsts.end(), std::back_inserter(unused)); return unused; } @@ -231,11 +240,6 @@ return instMan_->getInstrumentNameList(); } -std::vector> BambooTracker::checkDuplicateInstruments() const -{ - return instMan_->checkDuplicateInstruments(); -} - //--- FM void BambooTracker::setEnvelopeFMParameter(int envNum, FMEnvelopeParameter param, int value) { @@ -255,7 +259,7 @@ opnaCtrl_->updateInstrumentFM(instNum); } -std::vector BambooTracker::getEnvelopeFMUsers(int envNum) const +std::multiset BambooTracker::getEnvelopeFMUsers(int envNum) const { return instMan_->getEnvelopeFMUsers(envNum); } @@ -278,34 +282,49 @@ opnaCtrl_->updateInstrumentFM(instNum); } -std::vector BambooTracker::getLFOFMUsers(int lfoNum) const +std::multiset BambooTracker::getLFOFMUsers(int lfoNum) const { return instMan_->getLFOFMUsers(lfoNum); } -void BambooTracker::addOperatorSequenceFMSequenceCommand(FMEnvelopeParameter param, int opSeqNum, int type, int data) +void BambooTracker::addOperatorSequenceFMSequenceData(FMEnvelopeParameter param, int opSeqNum, int data) { - instMan_->addOperatorSequenceFMSequenceCommand(param, opSeqNum, type, data); + instMan_->addOperatorSequenceFMSequenceData(param, opSeqNum, data); } -void BambooTracker::removeOperatorSequenceFMSequenceCommand(FMEnvelopeParameter param, int opSeqNum) +void BambooTracker::removeOperatorSequenceFMSequenceData(FMEnvelopeParameter param, int opSeqNum) { - instMan_->removeOperatorSequenceFMSequenceCommand(param, opSeqNum); + instMan_->removeOperatorSequenceFMSequenceData(param, opSeqNum); } -void BambooTracker::setOperatorSequenceFMSequenceCommand(FMEnvelopeParameter param, int opSeqNum, int cnt, int type, int data) +void BambooTracker::setOperatorSequenceFMSequenceData(FMEnvelopeParameter param, int opSeqNum, int cnt, int data) { - instMan_->setOperatorSequenceFMSequenceCommand(param, opSeqNum, cnt, type, data); + instMan_->setOperatorSequenceFMSequenceData(param, opSeqNum, cnt, data); } -void BambooTracker::setOperatorSequenceFMLoops(FMEnvelopeParameter param, int opSeqNum, std::vector begins, std::vector ends, std::vector times) +void BambooTracker::addOperatorSequenceFMLoop(FMEnvelopeParameter param, int opSeqNum, const InstrumentSequenceLoop& loop) { - instMan_->setOperatorSequenceFMLoops(param, opSeqNum, std::move(begins), std::move(ends), std::move(times)); + instMan_->addOperatorSequenceFMLoop(param, opSeqNum, loop); } -void BambooTracker::setOperatorSequenceFMRelease(FMEnvelopeParameter param, int opSeqNum, ReleaseType type, int begin) +void BambooTracker::removeOperatorSequenceFMLoop(FMEnvelopeParameter param, int opSeqNum, int begin, int end) { - instMan_->setOperatorSequenceFMRelease(param, opSeqNum, type, begin); + instMan_->removeOperatorSequenceFMLoop(param, opSeqNum, begin, end); +} + +void BambooTracker::changeOperatorSequenceFMLoop(FMEnvelopeParameter param, int opSeqNum, int prevBegin, int prevEnd, const InstrumentSequenceLoop& loop) +{ + instMan_->changeOperatorSequenceFMLoop(param, opSeqNum, prevBegin, prevEnd, loop); +} + +void BambooTracker::clearOperatorSequenceFMLoops(FMEnvelopeParameter param, int opSeqNum) +{ + instMan_->clearOperatorSequenceFMLoops(param, opSeqNum); +} + +void BambooTracker::setOperatorSequenceFMRelease(FMEnvelopeParameter param, int opSeqNum, const InstrumentSequenceRelease& release) +{ + instMan_->setOperatorSequenceFMRelease(param, opSeqNum, release); } void BambooTracker::setInstrumentFMOperatorSequence(int instNum, FMEnvelopeParameter param, int opSeqNum) @@ -320,7 +339,7 @@ opnaCtrl_->updateInstrumentFM(instNum); } -std::vector BambooTracker::getOperatorSequenceFMUsers(FMEnvelopeParameter param, int opSeqNum) const +std::multiset BambooTracker::getOperatorSequenceFMUsers(FMEnvelopeParameter param, int opSeqNum) const { return instMan_->getOperatorSequenceFMUsers(param, opSeqNum); } @@ -330,29 +349,44 @@ instMan_->setArpeggioFMType(arpNum, type); } -void BambooTracker::addArpeggioFMSequenceCommand(int arpNum, int type, int data) +void BambooTracker::addArpeggioFMSequenceData(int arpNum, int data) +{ + instMan_->addArpeggioFMSequenceData(arpNum, data); +} + +void BambooTracker::removeArpeggioFMSequenceData(int arpNum) { - instMan_->addArpeggioFMSequenceCommand(arpNum, type, data); + instMan_->removeArpeggioFMSequenceData(arpNum); } -void BambooTracker::removeArpeggioFMSequenceCommand(int arpNum) +void BambooTracker::setArpeggioFMSequenceData(int arpNum, int cnt, int data) { - instMan_->removeArpeggioFMSequenceCommand(arpNum); + instMan_->setArpeggioFMSequenceData(arpNum, cnt, data); } -void BambooTracker::setArpeggioFMSequenceCommand(int arpNum, int cnt, int type, int data) +void BambooTracker::addArpeggioFMLoop(int arpNum, const InstrumentSequenceLoop& loop) { - instMan_->setArpeggioFMSequenceCommand(arpNum, cnt, type, data); + instMan_->addArpeggioFMLoop(arpNum, loop); } -void BambooTracker::setArpeggioFMLoops(int arpNum, std::vector begins, std::vector ends, std::vector times) +void BambooTracker::removeArpeggioFMLoop(int arpNum, int begin, int end) { - instMan_->setArpeggioFMLoops(arpNum, std::move(begins), std::move(ends), std::move(times)); + instMan_->removeArpeggioFMLoop(arpNum, begin, end); } -void BambooTracker::setArpeggioFMRelease(int arpNum, ReleaseType type, int begin) +void BambooTracker::changeArpeggioFMLoop(int arpNum, int prevBegin, int prevEnd, const InstrumentSequenceLoop& loop) { - instMan_->setArpeggioFMRelease(arpNum, type, begin); + instMan_->changeArpeggioFMLoop(arpNum, prevBegin, prevEnd, loop); +} + +void BambooTracker::clearArpeggioFMLoops(int arpNum) +{ + instMan_->clearArpeggioFMLoops(arpNum); +} + +void BambooTracker::setArpeggioFMRelease(int arpNum, const InstrumentSequenceRelease& release) +{ + instMan_->setArpeggioFMRelease(arpNum, release); } void BambooTracker::setInstrumentFMArpeggio(int instNum, FMOperatorType op, int arpNum) @@ -367,7 +401,7 @@ opnaCtrl_->updateInstrumentFM(instNum); } -std::vector BambooTracker::getArpeggioFMUsers(int arpNum) const +std::multiset BambooTracker::getArpeggioFMUsers(int arpNum) const { return instMan_->getArpeggioFMUsers(arpNum); } @@ -377,29 +411,44 @@ instMan_->setPitchFMType(ptNum, type); } -void BambooTracker::addPitchFMSequenceCommand(int ptNum, int type, int data) +void BambooTracker::addPitchFMSequenceData(int ptNum, int data) +{ + instMan_->addPitchFMSequenceData(ptNum, data); +} + +void BambooTracker::removePitchFMSequenceData(int ptNum) +{ + instMan_->removePitchFMSequenceData(ptNum); +} + +void BambooTracker::setPitchFMSequenceData(int ptNum, int cnt, int data) +{ + instMan_->setPitchFMSequenceData(ptNum, cnt, data); +} + +void BambooTracker::addPitchFMLoop(int ptNum, const InstrumentSequenceLoop& loop) { - instMan_->addPitchFMSequenceCommand(ptNum, type, data); + instMan_->addPitchFMLoop(ptNum, loop); } -void BambooTracker::removePitchFMSequenceCommand(int ptNum) +void BambooTracker::removePitchFMLoop(int ptNum, int begin, int end) { - instMan_->removePitchFMSequenceCommand(ptNum); + instMan_->removePitchFMLoop(ptNum, begin, end); } -void BambooTracker::setPitchFMSequenceCommand(int ptNum, int cnt, int type, int data) +void BambooTracker::changePitchFMLoop(int ptNum, int prevBegin, int prevEnd, const InstrumentSequenceLoop& loop) { - instMan_->setPitchFMSequenceCommand(ptNum, cnt, type, data); + instMan_->changePitchFMLoop(ptNum, prevBegin, prevEnd, loop); } -void BambooTracker::setPitchFMLoops(int ptNum, std::vector begins, std::vector ends, std::vector times) +void BambooTracker::clearPitchFMLoops(int ptNum) { - instMan_->setPitchFMLoops(ptNum, std::move(begins), std::move(ends), std::move(times)); + instMan_->clearPitchFMLoops(ptNum); } -void BambooTracker::setPitchFMRelease(int ptNum, ReleaseType type, int begin) +void BambooTracker::setPitchFMRelease(int ptNum, const InstrumentSequenceRelease& release) { - instMan_->setPitchFMRelease(ptNum, type, begin); + instMan_->setPitchFMRelease(ptNum, release); } void BambooTracker::setInstrumentFMPitch(int instNum, FMOperatorType op, int ptNum) @@ -414,7 +463,7 @@ opnaCtrl_->updateInstrumentFM(instNum); } -std::vector BambooTracker::getPitchFMUsers(int ptNum) const +std::multiset BambooTracker::getPitchFMUsers(int ptNum) const { return instMan_->getPitchFMUsers(ptNum); } @@ -426,29 +475,44 @@ } //--- SSG -void BambooTracker::addWaveformSSGSequenceCommand(int wfNum, int type, int data) +void BambooTracker::addWaveformSSGSequenceData(int wfNum, const SSGWaveformUnit& data) { - instMan_->addWaveformSSGSequenceCommand(wfNum, type, data); + instMan_->addWaveformSSGSequenceData(wfNum, data); } -void BambooTracker::removeWaveformSSGSequenceCommand(int wfNum) +void BambooTracker::removeWaveformSSGSequenceData(int wfNum) { - instMan_->removeWaveformSSGSequenceCommand(wfNum); + instMan_->removeWaveformSSGSequenceData(wfNum); } -void BambooTracker::setWaveformSSGSequenceCommand(int wfNum, int cnt, int type, int data) +void BambooTracker::setWaveformSSGSequenceData(int wfNum, int cnt, const SSGWaveformUnit& data) { - instMan_->setWaveformSSGSequenceCommand(wfNum, cnt, type, data); + instMan_->setWaveformSSGSequenceData(wfNum, cnt, data); } -void BambooTracker::setWaveformSSGLoops(int wfNum, std::vector begins, std::vector ends, std::vector times) +void BambooTracker::addWaveformSSGLoop(int wfNum, const InstrumentSequenceLoop& loop) { - instMan_->setWaveformSSGLoops(wfNum, std::move(begins), std::move(ends), std::move(times)); + instMan_->addWaveformSSGLoop(wfNum, loop); } -void BambooTracker::setWaveformSSGRelease(int wfNum, ReleaseType type, int begin) +void BambooTracker::removeWaveformSSGLoop(int wfNum, int begin, int end) { - instMan_->setWaveformSSGRelease(wfNum, type, begin); + instMan_->removeWaveformSSGLoop(wfNum, begin, end); +} + +void BambooTracker::changeWaveformSSGLoop(int wfNum, int prevBegin, int prevEnd, const InstrumentSequenceLoop& loop) +{ + instMan_->changeWaveformSSGLoop(wfNum, prevBegin, prevEnd, loop); +} + +void BambooTracker::clearWaveformSSGLoops(int wfNum) +{ + instMan_->clearWaveformSSGLoops(wfNum); +} + +void BambooTracker::setWaveformSSGRelease(int wfNum, const InstrumentSequenceRelease& release) +{ + instMan_->setWaveformSSGRelease(wfNum, release); } void BambooTracker::setInstrumentSSGWaveform(int instNum, int wfNum) @@ -463,34 +527,49 @@ opnaCtrl_->updateInstrumentSSG(instNum); } -std::vector BambooTracker::getWaveformSSGUsers(int wfNum) const +std::multiset BambooTracker::getWaveformSSGUsers(int wfNum) const { return instMan_->getWaveformSSGUsers(wfNum); } -void BambooTracker::addToneNoiseSSGSequenceCommand(int tnNum, int type, int data) +void BambooTracker::addToneNoiseSSGSequenceData(int tnNum, int data) +{ + instMan_->addToneNoiseSSGSequenceData(tnNum, data); +} + +void BambooTracker::removeToneNoiseSSGSequenceData(int tnNum) { - instMan_->addToneNoiseSSGSequenceCommand(tnNum, type, data); + instMan_->removeToneNoiseSSGSequenceData(tnNum); } -void BambooTracker::removeToneNoiseSSGSequenceCommand(int tnNum) +void BambooTracker::setToneNoiseSSGSequenceData(int tnNum, int cnt, int data) { - instMan_->removeToneNoiseSSGSequenceCommand(tnNum); + instMan_->setToneNoiseSSGSequenceData(tnNum, cnt, data); } -void BambooTracker::setToneNoiseSSGSequenceCommand(int tnNum, int cnt, int type, int data) +void BambooTracker::addToneNoiseSSGLoop(int tnNum, const InstrumentSequenceLoop& loop) { - instMan_->setToneNoiseSSGSequenceCommand(tnNum, cnt, type, data); + instMan_->addToneNoiseSSGLoop(tnNum, loop); } -void BambooTracker::setToneNoiseSSGLoops(int tnNum, std::vector begins, std::vector ends, std::vector times) +void BambooTracker::removeToneNoiseSSGLoop(int tnNum, int begin, int end) { - instMan_->setToneNoiseSSGLoops(tnNum, std::move(begins), std::move(ends), std::move(times)); + instMan_->removeToneNoiseSSGLoop(tnNum, begin, end); } -void BambooTracker::setToneNoiseSSGRelease(int tnNum, ReleaseType type, int begin) +void BambooTracker::changeToneNoiseSSGLoop(int tnNum, int prevBegin, int prevEnd, const InstrumentSequenceLoop& loop) { - instMan_->setToneNoiseSSGRelease(tnNum, type, begin); + instMan_->changeToneNoiseSSGLoop(tnNum, prevBegin, prevEnd, loop); +} + +void BambooTracker::clearToneNoiseSSGLoops(int tnNum) +{ + instMan_->clearToneNoiseSSGLoops(tnNum); +} + +void BambooTracker::setToneNoiseSSGRelease(int tnNum, const InstrumentSequenceRelease& release) +{ + instMan_->setToneNoiseSSGRelease(tnNum, release); } void BambooTracker::setInstrumentSSGToneNoise(int instNum, int tnNum) @@ -505,34 +584,49 @@ opnaCtrl_->updateInstrumentSSG(instNum); } -std::vector BambooTracker::getToneNoiseSSGUsers(int tnNum) const +std::multiset BambooTracker::getToneNoiseSSGUsers(int tnNum) const { return instMan_->getToneNoiseSSGUsers(tnNum); } -void BambooTracker::addEnvelopeSSGSequenceCommand(int envNum, int type, int data) +void BambooTracker::addEnvelopeSSGSequenceData(int envNum, const SSGEnvelopeUnit& data) { - instMan_->addEnvelopeSSGSequenceCommand(envNum, type, data); + instMan_->addEnvelopeSSGSequenceData(envNum, data); } -void BambooTracker::removeEnvelopeSSGSequenceCommand(int envNum) +void BambooTracker::removeEnvelopeSSGSequenceData(int envNum) { - instMan_->removeEnvelopeSSGSequenceCommand(envNum); + instMan_->removeEnvelopeSSGSequenceData(envNum); } -void BambooTracker::setEnvelopeSSGSequenceCommand(int envNum, int cnt, int type, int data) +void BambooTracker::setEnvelopeSSGSequenceData(int envNum, int cnt, const SSGEnvelopeUnit& data) { - instMan_->setEnvelopeSSGSequenceCommand(envNum, cnt, type, data); + instMan_->setEnvelopeSSGSequenceData(envNum, cnt, data); } -void BambooTracker::setEnvelopeSSGLoops(int envNum, std::vector begins, std::vector ends, std::vector times) +void BambooTracker::addEnvelopeSSGLoop(int envNum, const InstrumentSequenceLoop& loop) { - instMan_->setEnvelopeSSGLoops(envNum, std::move(begins), std::move(ends), std::move(times)); + instMan_->addEnvelopeSSGLoop(envNum, loop); } -void BambooTracker::setEnvelopeSSGRelease(int envNum, ReleaseType type, int begin) +void BambooTracker::removeEnvelopeSSGLoop(int envNum, int begin, int end) { - instMan_->setEnvelopeSSGRelease(envNum, type, begin); + instMan_->removeEnvelopeSSGLoop(envNum, begin, end); +} + +void BambooTracker::changeEnvelopeSSGLoop(int envNum, int prevBegin, int prevEnd, const InstrumentSequenceLoop& loop) +{ + instMan_->changeEnvelopeSSGLoop(envNum, prevBegin, prevEnd, loop); +} + +void BambooTracker::clearEnvelopeSSGLoops(int envNum) +{ + instMan_->clearEnvelopeSSGLoops(envNum); +} + +void BambooTracker::setEnvelopeSSGRelease(int envNum, const InstrumentSequenceRelease& release) +{ + instMan_->setEnvelopeSSGRelease(envNum, release); } void BambooTracker::setInstrumentSSGEnvelope(int instNum, int envNum) @@ -547,7 +641,7 @@ opnaCtrl_->updateInstrumentSSG(instNum); } -std::vector BambooTracker::getEnvelopeSSGUsers(int envNum) const +std::multiset BambooTracker::getEnvelopeSSGUsers(int envNum) const { return instMan_->getEnvelopeSSGUsers(envNum); } @@ -557,29 +651,44 @@ instMan_->setArpeggioSSGType(arpNum, type); } -void BambooTracker::addArpeggioSSGSequenceCommand(int arpNum, int type, int data) +void BambooTracker::addArpeggioSSGSequenceData(int arpNum, int data) +{ + instMan_->addArpeggioSSGSequenceData(arpNum, data); +} + +void BambooTracker::removeArpeggioSSGSequenceData(int arpNum) +{ + instMan_->removeArpeggioSSGSequenceData(arpNum); +} + +void BambooTracker::setArpeggioSSGSequenceData(int arpNum, int cnt, int data) +{ + instMan_->setArpeggioSSGSequenceData(arpNum, cnt, data); +} + +void BambooTracker::addArpeggioSSGLoop(int arpNum, const InstrumentSequenceLoop& loop) { - instMan_->addArpeggioSSGSequenceCommand(arpNum, type, data); + instMan_->addArpeggioSSGLoop(arpNum, loop); } -void BambooTracker::removeArpeggioSSGSequenceCommand(int arpNum) +void BambooTracker::removeArpeggioSSGLoop(int arpNum, int begin, int end) { - instMan_->removeArpeggioSSGSequenceCommand(arpNum); + instMan_->removeArpeggioSSGLoop(arpNum, begin, end); } -void BambooTracker::setArpeggioSSGSequenceCommand(int arpNum, int cnt, int type, int data) +void BambooTracker::changeArpeggioSSGLoop(int arpNum, int prevBegin, int prevEnd, const InstrumentSequenceLoop& loop) { - instMan_->setArpeggioSSGSequenceCommand(arpNum, cnt, type, data); + instMan_->changeArpeggioSSGLoop(arpNum, prevBegin, prevEnd, loop); } -void BambooTracker::setArpeggioSSGLoops(int arpNum, std::vector begins, std::vector ends, std::vector times) +void BambooTracker::clearArpeggioSSGLoops(int arpNum) { - instMan_->setArpeggioSSGLoops(arpNum, std::move(begins), std::move(ends), std::move(times)); + instMan_->clearArpeggioSSGLoops(arpNum); } -void BambooTracker::setArpeggioSSGRelease(int arpNum, ReleaseType type, int begin) +void BambooTracker::setArpeggioSSGRelease(int arpNum, const InstrumentSequenceRelease& release) { - instMan_->setArpeggioSSGRelease(arpNum, type, begin); + instMan_->setArpeggioSSGRelease(arpNum, release); } void BambooTracker::setInstrumentSSGArpeggio(int instNum, int arpNum) @@ -594,7 +703,7 @@ opnaCtrl_->updateInstrumentSSG(instNum); } -std::vector BambooTracker::getArpeggioSSGUsers(int arpNum) const +std::multiset BambooTracker::getArpeggioSSGUsers(int arpNum) const { return instMan_->getArpeggioSSGUsers(arpNum); } @@ -604,29 +713,44 @@ instMan_->setPitchSSGType(ptNum, type); } -void BambooTracker::addPitchSSGSequenceCommand(int ptNum, int type, int data) +void BambooTracker::addPitchSSGSequenceData(int ptNum, int data) +{ + instMan_->addPitchSSGSequenceData(ptNum, data); +} + +void BambooTracker::removePitchSSGSequenceData(int ptNum) { - instMan_->addPitchSSGSequenceCommand(ptNum, type, data); + instMan_->removePitchSSGSequenceData(ptNum); } -void BambooTracker::removePitchSSGSequenceCommand(int ptNum) +void BambooTracker::setPitchSSGSequenceData(int ptNum, int cnt, int data) { - instMan_->removePitchSSGSequenceCommand(ptNum); + instMan_->setPitchSSGSequenceData(ptNum, cnt, data); } -void BambooTracker::setPitchSSGSequenceCommand(int ptNum, int cnt, int type, int data) +void BambooTracker::addPitchSSGLoop(int ptNum, const InstrumentSequenceLoop& loop) { - instMan_->setPitchSSGSequenceCommand(ptNum, cnt, type, data); + instMan_->addPitchSSGLoop(ptNum, loop); } -void BambooTracker::setPitchSSGLoops(int ptNum, std::vector begins, std::vector ends, std::vector times) +void BambooTracker::removePitchSSGLoop(int ptNum, int begin, int end) { - instMan_->setPitchSSGLoops(ptNum, std::move(begins), std::move(ends), std::move(times)); + instMan_->removePitchSSGLoop(ptNum, begin, end); } -void BambooTracker::setPitchSSGRelease(int ptNum, ReleaseType type, int begin) +void BambooTracker::changePitchSSGLoop(int ptNum, int prevBegin, int prevEnd, const InstrumentSequenceLoop& loop) { - instMan_->setPitchSSGRelease(ptNum, type, begin); + instMan_->changePitchSSGLoop(ptNum, prevBegin, prevEnd, loop); +} + +void BambooTracker::clearPitchSSGLoops(int ptNum) +{ + instMan_->clearPitchSSGLoops(ptNum); +} + +void BambooTracker::setPitchSSGRelease(int ptNum, const InstrumentSequenceRelease& release) +{ + instMan_->setPitchSSGRelease(ptNum, release); } void BambooTracker::setInstrumentSSGPitch(int instNum, int ptNum) @@ -641,7 +765,7 @@ opnaCtrl_->updateInstrumentSSG(instNum); } -std::vector BambooTracker::getPitchSSGUsers(int ptNum) const +std::multiset BambooTracker::getPitchSSGUsers(int ptNum) const { return instMan_->getPitchSSGUsers(ptNum); } @@ -691,11 +815,16 @@ return instMan_->isSampleADPCMRepeatable(sampNum); } -void BambooTracker::storeSampleADPCMRawSample(int sampNum, std::vector sample) +void BambooTracker::storeSampleADPCMRawSample(int sampNum, const std::vector& sample) { instMan_->storeSampleADPCMRawSample(sampNum, sample); } +void BambooTracker::storeSampleADPCMRawSample(int sampNum, std::vector&& sample) +{ + instMan_->storeSampleADPCMRawSample(sampNum, std::move(sample)); +} + std::vector BambooTracker::getSampleADPCMRawSample(int sampNum) const { return instMan_->getSampleADPCMRawSample(sampNum); @@ -706,17 +835,23 @@ instMan_->clearSampleADPCMRawSample(sampNum); } -void BambooTracker::assignSampleADPCMRawSamples() +bool BambooTracker::assignSampleADPCMRawSamples() { opnaCtrl_->clearSamplesADPCM(); std::vector idcs = storeOnlyUsedSamples_ ? instMan_->getSampleADPCMValidIndices() : instMan_->getSampleADPCMEntriedIndices(); + bool storedAll = true; for (auto sampNum : idcs) { - std::vector addresses - = opnaCtrl_->storeSampleADPCM(instMan_->getSampleADPCMRawSample(sampNum)); - instMan_->setSampleADPCMStartAddress(sampNum, addresses[0]); - instMan_->setSampleADPCMStopAddress(sampNum, addresses[1]); + size_t startAddr, stopAddr; + if (opnaCtrl_->storeSampleADPCM(instMan_->getSampleADPCMRawSample(sampNum), startAddr, stopAddr)) { + instMan_->setSampleADPCMStartAddress(sampNum, startAddr); + instMan_->setSampleADPCMStopAddress(sampNum, stopAddr); + } + else { + storedAll = false; + } } + return storedAll; } size_t BambooTracker::getSampleADPCMStartAddress(int sampNum) const @@ -735,34 +870,49 @@ opnaCtrl_->updateInstrumentADPCM(instNum); } -std::vector BambooTracker::getSampleADPCMUsers(int sampNum) const +std::multiset BambooTracker::getSampleADPCMUsers(int sampNum) const { return instMan_->getSampleADPCMUsers(sampNum); } -void BambooTracker::addEnvelopeADPCMSequenceCommand(int envNum, int type, int data) +void BambooTracker::addEnvelopeADPCMSequenceData(int envNum, int data) +{ + instMan_->addEnvelopeADPCMSequenceData(envNum, data); +} + +void BambooTracker::removeEnvelopeADPCMSequenceData(int envNum) { - instMan_->addEnvelopeADPCMSequenceCommand(envNum, type, data); + instMan_->removeEnvelopeADPCMSequenceData(envNum); } -void BambooTracker::removeEnvelopeADPCMSequenceCommand(int envNum) +void BambooTracker::setEnvelopeADPCMSequenceData(int envNum, int cnt, int data) { - instMan_->removeEnvelopeADPCMSequenceCommand(envNum); + instMan_->setEnvelopeADPCMSequenceData(envNum, cnt, data); } -void BambooTracker::setEnvelopeADPCMSequenceCommand(int envNum, int cnt, int type, int data) +void BambooTracker::addEnvelopeADPCMLoop(int arpNum, const InstrumentSequenceLoop& loop) { - instMan_->setEnvelopeADPCMSequenceCommand(envNum, cnt, type, data); + instMan_->addEnvelopeADPCMLoop(arpNum, loop); } -void BambooTracker::setEnvelopeADPCMLoops(int envNum, std::vector begins, std::vector ends, std::vector times) +void BambooTracker::removeEnvelopeADPCMLoop(int envNum, int begin, int end) { - instMan_->setEnvelopeADPCMLoops(envNum, std::move(begins), std::move(ends), std::move(times)); + instMan_->removeEnvelopeADPCMLoop(envNum, begin, end); } -void BambooTracker::setEnvelopeADPCMRelease(int envNum, ReleaseType type, int begin) +void BambooTracker::changeEnvelopeADPCMLoop(int envNum, int prevBegin, int prevEnd, const InstrumentSequenceLoop& loop) { - instMan_->setEnvelopeADPCMRelease(envNum, type, begin); + instMan_->changeEnvelopeADPCMLoop(envNum, prevBegin, prevEnd, loop); +} + +void BambooTracker::clearEnvelopeADPCMLoops(int envNum) +{ + instMan_->clearEnvelopeADPCMLoops(envNum); +} + +void BambooTracker::setEnvelopeADPCMRelease(int arpNum, const InstrumentSequenceRelease& release) +{ + instMan_->setEnvelopeADPCMRelease(arpNum, release); } void BambooTracker::setInstrumentADPCMEnvelope(int instNum, int envNum) @@ -777,7 +927,7 @@ opnaCtrl_->updateInstrumentADPCM(instNum); } -std::vector BambooTracker::getEnvelopeADPCMUsers(int envNum) const +std::multiset BambooTracker::getEnvelopeADPCMUsers(int envNum) const { return instMan_->getEnvelopeADPCMUsers(envNum); } @@ -787,29 +937,44 @@ instMan_->setArpeggioADPCMType(arpNum, type); } -void BambooTracker::addArpeggioADPCMSequenceCommand(int arpNum, int type, int data) +void BambooTracker::addArpeggioADPCMSequenceData(int arpNum, int data) +{ + instMan_->addArpeggioADPCMSequenceData(arpNum, data); +} + +void BambooTracker::removeArpeggioADPCMSequenceData(int arpNum) +{ + instMan_->removeArpeggioADPCMSequenceData(arpNum); +} + +void BambooTracker::setArpeggioADPCMSequenceData(int arpNum, int cnt, int data) +{ + instMan_->setArpeggioADPCMSequenceData(arpNum, cnt, data); +} + +void BambooTracker::addArpeggioADPCMLoop(int arpNum, const InstrumentSequenceLoop& loop) { - instMan_->addArpeggioADPCMSequenceCommand(arpNum, type, data); + instMan_->addArpeggioADPCMLoop(arpNum, loop); } -void BambooTracker::removeArpeggioADPCMSequenceCommand(int arpNum) +void BambooTracker::removeArpeggioADPCMLoop(int arpNum, int begin, int end) { - instMan_->removeArpeggioADPCMSequenceCommand(arpNum); + instMan_->removeArpeggioADPCMLoop(arpNum, begin, end); } -void BambooTracker::setArpeggioADPCMSequenceCommand(int arpNum, int cnt, int type, int data) +void BambooTracker::changeArpeggioADPCMLoop(int arpNum, int prevBegin, int prevEnd, const InstrumentSequenceLoop& loop) { - instMan_->setArpeggioADPCMSequenceCommand(arpNum, cnt, type, data); + instMan_->changeArpeggioADPCMLoop(arpNum, prevBegin, prevEnd, loop); } -void BambooTracker::setArpeggioADPCMLoops(int arpNum, std::vector begins, std::vector ends, std::vector times) +void BambooTracker::clearArpeggioADPCMLoops(int arpNum) { - instMan_->setArpeggioADPCMLoops(arpNum, std::move(begins), std::move(ends), std::move(times)); + instMan_->clearArpeggioADPCMLoops(arpNum); } -void BambooTracker::setArpeggioADPCMRelease(int arpNum, ReleaseType type, int begin) +void BambooTracker::setArpeggioADPCMRelease(int arpNum, const InstrumentSequenceRelease& release) { - instMan_->setArpeggioADPCMRelease(arpNum, type, begin); + instMan_->setArpeggioADPCMRelease(arpNum, release); } void BambooTracker::setInstrumentADPCMArpeggio(int instNum, int arpNum) @@ -824,7 +989,7 @@ opnaCtrl_->updateInstrumentADPCM(instNum); } -std::vector BambooTracker::getArpeggioADPCMUsers(int arpNum) const +std::multiset BambooTracker::getArpeggioADPCMUsers(int arpNum) const { return instMan_->getArpeggioADPCMUsers(arpNum); } @@ -834,29 +999,44 @@ instMan_->setPitchADPCMType(ptNum, type); } -void BambooTracker::addPitchADPCMSequenceCommand(int ptNum, int type, int data) +void BambooTracker::addPitchADPCMSequenceData(int ptNum, int data) { - instMan_->addPitchADPCMSequenceCommand(ptNum, type, data); + instMan_->addPitchADPCMSequenceData(ptNum, data); } -void BambooTracker::removePitchADPCMSequenceCommand(int ptNum) +void BambooTracker::removePitchADPCMSequenceData(int ptNum) { - instMan_->removePitchADPCMSequenceCommand(ptNum); + instMan_->removePitchADPCMSequenceData(ptNum); } -void BambooTracker::setPitchADPCMSequenceCommand(int ptNum, int cnt, int type, int data) +void BambooTracker::setPitchADPCMSequenceData(int ptNum, int cnt, int data) { - instMan_->setPitchADPCMSequenceCommand(ptNum, cnt, type, data); + instMan_->setPitchADPCMSequenceData(ptNum, cnt, data); } -void BambooTracker::setPitchADPCMLoops(int ptNum, std::vector begins, std::vector ends, std::vector times) +void BambooTracker::addPitchADPCMLoop(int ptNum, const InstrumentSequenceLoop& loop) { - instMan_->setPitchADPCMLoops(ptNum, std::move(begins), std::move(ends), std::move(times)); + instMan_->addPitchADPCMLoop(ptNum, loop); } -void BambooTracker::setPitchADPCMRelease(int ptNum, ReleaseType type, int begin) +void BambooTracker::removePitchADPCMLoop(int ptNum, int begin, int end) { - instMan_->setPitchADPCMRelease(ptNum, type, begin); + instMan_->removePitchADPCMLoop(ptNum, begin, end); +} + +void BambooTracker::changePitchADPCMLoop(int ptNum, int prevBegin, int prevEnd, const InstrumentSequenceLoop& loop) +{ + instMan_->changePitchADPCMLoop(ptNum, prevBegin, prevEnd, loop); +} + +void BambooTracker::clearPitchADPCMLoops(int ptNum) +{ + instMan_->clearPitchADPCMLoops(ptNum); +} + +void BambooTracker::setPitchADPCMRelease(int ptNum, const InstrumentSequenceRelease& release) +{ + instMan_->setPitchADPCMRelease(ptNum, release); } void BambooTracker::setInstrumentADPCMPitch(int instNum, int ptNum) @@ -871,7 +1051,7 @@ opnaCtrl_->updateInstrumentADPCM(instNum); } -std::vector BambooTracker::getPitchADPCMUsers(int ptNum) const +std::multiset BambooTracker::getPitchADPCMUsers(int ptNum) const { return instMan_->getPitchADPCMUsers(ptNum); } @@ -923,19 +1103,19 @@ tickCounter_->resetCount(); tickCounter_->setTempo(song.getTempo()); tickCounter_->setSpeed(song.getSpeed()); - tickCounter_->setGroove(mod_->getGroove(song.getGroove()).getSequence()); + tickCounter_->setGroove(mod_->getGroove(song.getGroove())); tickCounter_->setGrooveState(song.isUsedTempo() ? GrooveState::Invalid : GrooveState::ValidByGlobal); std::unordered_map pairs = { - { SoundSource::FM, getFMChannelCount(songStyle_.type) }, + { SoundSource::FM, Song::getFMChannelCount(songStyle_.type) }, { SoundSource::SSG, 3 }, { SoundSource::RHYTHM, 6 }, { SoundSource::ADPCM, 1 }, }; for (auto& pair : pairs) { muteState_[pair.first] = std::vector(pair.second, false); - for (int i = 0; i < pair.second; ++i) opnaCtrl_->setMuteState(pair.first, i, false); + for (int ch = 0; ch < pair.second; ++ch) opnaCtrl_->setMuteState(pair.first, ch, false); } } @@ -995,7 +1175,7 @@ void BambooTracker::jamKeyOn(JamKey key, bool volumeSet) { - int keyNum = octaveAndNoteToNoteNumber(curOctave_, jam_utils::jamKeyToNote(key)); + int keyNum = jam_utils::makeNote(curOctave_, key).getNoteNumber(); const TrackAttribute& attrib = songStyle_.trackAttribs[static_cast(curTrackNum_)]; funcJamKeyOn(key, keyNum, attrib, volumeSet); } @@ -1008,14 +1188,13 @@ void BambooTracker::jamKeyOnForced(JamKey key, SoundSource src, bool volumeSet, std::shared_ptr inst) { - int keyNum = octaveAndNoteToNoteNumber(curOctave_, jam_utils::jamKeyToNote(key)); + int keyNum = jam_utils::makeNote(curOctave_, key).getNoteNumber(); const TrackAttribute& attrib = songStyle_.trackAttribs[static_cast(curTrackNum_)]; if (attrib.source == src) { funcJamKeyOn(key, keyNum, attrib, volumeSet, inst); } else { - auto it = std::find_if(songStyle_.trackAttribs.begin(), songStyle_.trackAttribs.end(), - [src](TrackAttribute& attrib) { return attrib.source == src; }); + auto it = utils::findIf(songStyle_.trackAttribs, [src](const TrackAttribute& attrib) { return attrib.source == src; }); funcJamKeyOn(key, keyNum, *it, volumeSet, inst); } } @@ -1027,8 +1206,7 @@ funcJamKeyOn(JamKey::MidiKey, keyNum, attrib, volumeSet, inst); } else { - auto it = std::find_if(songStyle_.trackAttribs.begin(), songStyle_.trackAttribs.end(), - [src](TrackAttribute& attrib) { return attrib.source == src; }); + auto it = utils::findIf(songStyle_.trackAttribs, [src](const TrackAttribute& attrib) { return attrib.source == src; }); funcJamKeyOn(JamKey::MidiKey, keyNum, *it, volumeSet, inst); } } @@ -1040,7 +1218,7 @@ if (attrib.source == SoundSource::RHYTHM) { if (volumeSet) - opnaCtrl_->setVolumeRhythm(attrib.channelInSource, std::min(curVolume_, 0x1f)); + opnaCtrl_->setVolumeRhythm(attrib.channelInSource, std::min(curVolume_, bt_defs::NSTEP_RHYTHM_VOLUME - 1)); opnaCtrl_->setKeyOnFlagRhythm(attrib.channelInSource); opnaCtrl_->updateRegisterStates(); } @@ -1076,22 +1254,7 @@ } JamKeyInfo& onInfo = list.front(); - Note note; - int octave, pitch; - if (key == JamKey::MidiKey) { - auto octNote = noteNumberToOctaveAndNote(onInfo.keyNum); - note = octNote.second; - octave = octNote.first; - } - else { - note = jam_utils::jamKeyToNote(onInfo.key); - octave = jam_utils::calculateJamKeyOctave(curOctave_, onInfo.key); - if (octave > 7) { // Tone range check - octave = 7; - note = Note::B; - } - } - pitch = 0; + Note&& note = jam_utils::makeNote(onInfo, curOctave_); switch (onInfo.source) { case SoundSource::FM: @@ -1099,18 +1262,18 @@ opnaCtrl_->setInstrumentFM(onInfo.channelInSource, fm); if (volumeSet) { int vol; - if (volFMReversed_) vol = (curVolume_ < 0x80) ? (0x7f - curVolume_) : 0; - else vol = std::min(curVolume_, 0x7f); + if (volFMReversed_) vol = effect_utils::reverseFmVolume(curVolume_, true); + else vol = std::min(curVolume_, bt_defs::NSTEP_FM_VOLUME - 1); opnaCtrl_->setVolumeFM(onInfo.channelInSource, vol); } if (songStyle_.type == SongType::FM3chExpanded && onInfo.channelInSource == 2) { - opnaCtrl_->keyOnFM(2, note, octave, pitch, true); - opnaCtrl_->keyOnFM(6, note, octave, pitch, true); - opnaCtrl_->keyOnFM(7, note, octave, pitch, true); - opnaCtrl_->keyOnFM(8, note, octave, pitch, true); + opnaCtrl_->keyOnFM(2, note, true); + opnaCtrl_->keyOnFM(6, note, true); + opnaCtrl_->keyOnFM(7, note, true); + opnaCtrl_->keyOnFM(8, note, true); } else { - opnaCtrl_->keyOnFM(onInfo.channelInSource, note, octave, pitch, true); + opnaCtrl_->keyOnFM(onInfo.channelInSource, note, true); } break; case SoundSource::SSG: @@ -1118,7 +1281,7 @@ opnaCtrl_->setInstrumentSSG(onInfo.channelInSource, ssg); if (volumeSet) opnaCtrl_->setVolumeSSG(onInfo.channelInSource, std::min(curVolume_, 0xf)); - opnaCtrl_->keyOnSSG(onInfo.channelInSource, note, octave, pitch, true); + opnaCtrl_->keyOnSSG(onInfo.channelInSource, note, true); break; case SoundSource::ADPCM: if (auto adpcm = std::dynamic_pointer_cast(inst)) @@ -1126,7 +1289,7 @@ else if (auto kit = std::dynamic_pointer_cast(inst)) opnaCtrl_->setInstrumentDrumkit(kit); if (volumeSet) opnaCtrl_->setVolumeADPCM(curVolume_); - opnaCtrl_->keyOnADPCM(note, octave, pitch, true); + opnaCtrl_->keyOnADPCM(note, true); break; default: break; @@ -1136,7 +1299,7 @@ void BambooTracker::jamKeyOff(JamKey key) { - int keyNum = octaveAndNoteToNoteNumber(curOctave_, jam_utils::jamKeyToNote(key)); + int keyNum = jam_utils::makeNote(curOctave_, key).getNoteNumber(); const TrackAttribute& attrib = songStyle_.trackAttribs[static_cast(curTrackNum_)]; funcJamKeyOff(key, keyNum, attrib); } @@ -1149,14 +1312,13 @@ void BambooTracker::jamKeyOffForced(JamKey key, SoundSource src) { - int keyNum = octaveAndNoteToNoteNumber(curOctave_, jam_utils::jamKeyToNote(key)); + int keyNum = jam_utils::makeNote(curOctave_, key).getNoteNumber(); const TrackAttribute& attrib = songStyle_.trackAttribs[static_cast(curTrackNum_)]; if (attrib.source == src) { funcJamKeyOff(key, keyNum, attrib); } else { - auto it = std::find_if(songStyle_.trackAttribs.begin(), songStyle_.trackAttribs.end(), - [src](TrackAttribute& attrib) { return attrib.source == src; }); + auto it = utils::findIf(songStyle_.trackAttribs, [src](const TrackAttribute& attrib) { return attrib.source == src; }); funcJamKeyOff(key, keyNum, *it); } } @@ -1168,8 +1330,7 @@ funcJamKeyOff(JamKey::MidiKey, keyNum, attrib); } else { - auto it = std::find_if(songStyle_.trackAttribs.begin(), songStyle_.trackAttribs.end(), - [src](TrackAttribute& attrib) { return attrib.source == src; }); + auto it = utils::findIf(songStyle_.trackAttribs, [src](const TrackAttribute& attrib) { return attrib.source == src; }); funcJamKeyOff(JamKey::MidiKey, keyNum, *it); } } @@ -1209,30 +1370,42 @@ } } -std::vector> BambooTracker::assignADPCMBeforeForcedJamKeyOn(std::shared_ptr inst) +bool BambooTracker::assignADPCMBeforeForcedJamKeyOn( + std::shared_ptr inst, std::unordered_map>& sampAddrs) { + size_t start, stop; + bool isAssignedAll = false; switch (inst->getType()) { case InstrumentType::ADPCM: { opnaCtrl_->clearSamplesADPCM(); - return { opnaCtrl_->storeSampleADPCM( - std::dynamic_pointer_cast(inst)->getRawSample()) }; + if (opnaCtrl_->storeSampleADPCM( + std::dynamic_pointer_cast(inst)->getRawSample(), start, stop)) { + sampAddrs[0] = { start, stop }; + isAssignedAll = true; + } + break; } case InstrumentType::Drumkit: { opnaCtrl_->clearSamplesADPCM(); std::vector> addrs; + auto kit = std::dynamic_pointer_cast(inst); for (const int& key : kit->getAssignedKeys()) { - int samp = kit->getSampleNumber(key); - if (addrs.size() <= static_cast(samp)) addrs.resize(samp + 1); - if (addrs[samp].empty()) addrs[samp] = opnaCtrl_->storeSampleADPCM(kit->getRawSample(key)); + int n = kit->getSampleNumber(key); + if (!sampAddrs.count(n)) { + bool assigned = opnaCtrl_->storeSampleADPCM(kit->getRawSample(key), start, stop); + if (assigned) sampAddrs[n] = { start, stop }; + isAssignedAll &= assigned; + } } - return addrs; + break; } default: - return {}; + break; } + return isAssignedAll; } /********** Play song **********/ @@ -1382,7 +1555,61 @@ } /********** Export **********/ -bool BambooTracker::exportToWav(io::WavContainer& container, int loopCnt, std::function bar) +namespace +{ +void checkNextPositionOfLastStepAndStepSize(Song& song, int& endOrder, int& endStep) +{ + endOrder = 0; + endStep = 0; + + std::vector attribs = song.getTrackAttributes(); + std::unordered_set orderStepMap; + int lastOrder = static_cast(song.getOrderSize()) - 1; + + for (int curOrder = 0; !orderStepMap.count(curOrder); curOrder = endOrder) { + orderStepMap.insert(curOrder); // Arrived flag + // Default next order + endOrder = (endOrder + 1) % (lastOrder + 1); + endStep = 0; + + // Check jump effect + for (auto attrib : attribs) { + Step& step = song.getTrack(attrib.number).getPatternFromOrderNumber(curOrder) + .getStep(static_cast(song.getPatternSizeFromOrderNumber(curOrder)) - 1); + for (int i = 0; i < Step::N_EFFECT; ++i) { + Effect&& eff = effect_utils::validateEffect(attrib.source, step.getEffect(i)); + switch (eff.type) { + case EffectType::PositionJump: + if (eff.value <= lastOrder) { + endOrder = eff.value; + endStep = 0; + } + break; + case EffectType::SongEnd: + endOrder = -1; + endStep = -1; + return; + case EffectType::PatternBreak: + if (curOrder == lastOrder + && eff.value < static_cast(song.getPatternSizeFromOrderNumber(0))) { + endOrder = 0; + endStep = eff.value; + } + else if (eff.value < static_cast(song.getPatternSizeFromOrderNumber(curOrder + 1))) { + endOrder = curOrder + 1; + endStep = eff.value; + } + break; + default: + break; + } + } + } + } +} +} + +bool BambooTracker::exportToWav(io::WavContainer& container, int loopCnt, ExportCancellCallback checkFunc) { int tmpRate = opnaCtrl_->getRate(); opnaCtrl_->setRate(static_cast(container.getSampleRate())); @@ -1391,10 +1618,8 @@ size_t intrCntRest = 0; std::vector buf(sampCnt << 1); - int endOrder = 0; - int endStep = 0; - size_t dummy = 0; - checkNextPositionOfLastStepAndStepSize(curSongNum_, endOrder, endStep, dummy, dummy); + int endOrder, endStep; + checkNextPositionOfLastStepAndStepSize(mod_->getSong(curSongNum_), endOrder, endStep); bool endFlag = false; bool tmpFollow = std::exchange(isFollowPlay_, false); startPlayFromStart(); @@ -1406,7 +1631,7 @@ intrCntRest = intrCnt; // Set counts to next interruption if (!streamCountUp()) { - if (bar()) { // Update lambda function + if (checkFunc()) { // Update lambda function stopPlaySong(); isFollowPlay_ = tmpFollow; return false; @@ -1441,7 +1666,7 @@ } bool BambooTracker::exportToVgm(io::BinaryContainer& container, int target, bool gd3TagEnabled, - io::GD3Tag tag, std::function bar) + const io::GD3Tag& tag, ExportCancellCallback checkFunc) { int tmpRate = opnaCtrl_->getRate(); opnaCtrl_->setRate(44100); @@ -1450,10 +1675,8 @@ double intrCntDiff = dblIntrCnt - intrCnt; double intrCntRest = 0; - int loopOrder = 0; - int loopStep = 0; - size_t dummy = 0; - checkNextPositionOfLastStepAndStepSize(curSongNum_, loopOrder, loopStep, dummy, dummy); + int loopOrder, loopStep; + checkNextPositionOfLastStepAndStepSize(mod_->getSong(curSongNum_), loopOrder, loopStep); bool loopFlag = (loopOrder != -1); int endCnt = (loopOrder == -1) ? 0 : 1; bool tmpFollow = std::exchange(isFollowPlay_, false); @@ -1466,13 +1689,14 @@ opnaCtrl_->clearSamplesADPCM(); std::vector rom; for (auto sampNum : instMan_->getSampleADPCMValidIndices()) { - std::vector sample = instMan_->getSampleADPCMRawSample(sampNum); - std::vector addresses = opnaCtrl_->storeSampleADPCM(sample); - instMan_->setSampleADPCMStartAddress(sampNum, addresses[0]); - instMan_->setSampleADPCMStopAddress(sampNum, addresses[1]); - - rom.resize((addresses[1] + 1) << 5); - std::copy(sample.begin(), sample.end(), rom.begin() + static_cast(addresses[0] << 5)); + std::vector&& sample = instMan_->getSampleADPCMRawSample(sampNum); + size_t startAddr, stopAddr; + if (opnaCtrl_->storeSampleADPCM(sample, startAddr, stopAddr)) { + instMan_->setSampleADPCMStartAddress(sampNum, startAddr); + instMan_->setSampleADPCMStopAddress(sampNum, stopAddr); + rom.resize((stopAddr + 1) << 5); + std::copy(sample.begin(), sample.end(), rom.begin() + static_cast(startAddr << 5)); + } } exCntr->setDataBlock(std::move(rom)); @@ -1482,7 +1706,7 @@ while (true) { if (!streamCountUp()) { - if (bar()) { // Update lambda function + if (checkFunc()) { // Update lambda function stopPlaySong(); isFollowPlay_ = tmpFollow; return false; @@ -1521,7 +1745,7 @@ } bool BambooTracker::exportToS98(io::BinaryContainer& container, int target, bool tagEnabled, - io::S98Tag tag, int rate, std::function bar) + const io::S98Tag& tag, int rate, ExportCancellCallback checkFunc) { int tmpRate = opnaCtrl_->getRate(); opnaCtrl_->setRate(rate); @@ -1530,10 +1754,8 @@ double intrCntDiff = dblIntrCnt - intrCnt; double intrCntRest = 0; - int loopOrder = 0; - int loopStep = 0; - size_t dummy = 0; - checkNextPositionOfLastStepAndStepSize(curSongNum_, loopOrder, loopStep, dummy, dummy); + int loopOrder, loopStep; + checkNextPositionOfLastStepAndStepSize(mod_->getSong(curSongNum_), loopOrder, loopStep); bool loopFlag = (loopOrder != -1); int endCnt = (loopOrder == -1) ? 0 : 1; bool tmpFollow = std::exchange(isFollowPlay_, false); @@ -1547,7 +1769,7 @@ while (true) { exCntr->getData(); // Set wait counts if (!streamCountUp()) { - if (bar()) { // Update lambda function + if (checkFunc()) { // Update lambda function stopPlaySong(); isFollowPlay_ = tmpFollow; return false; @@ -1582,68 +1804,6 @@ } } -void BambooTracker::checkNextPositionOfLastStepAndStepSize(int songNum, int& endOrder, int& endStep, size_t& nIntroStep, size_t& nLoopStep) const -{ - Song& song = mod_->getSong(songNum); - endOrder = 0; - endStep = 0; - - int lastOrder = static_cast(song.getOrderSize()) - 1; - std::unordered_map orderStepMap; - int orderN = 0; - size_t stepCnt = 0; - do { - endOrder = (endOrder + 1) % (lastOrder + 1); - endStep = 0; - - int stepN = static_cast(getPatternSizeFromOrderNumber(songNum, orderN)) - 1; - for (auto attrib : songStyle_.trackAttribs) { - Step& step = song.getTrack(attrib.number).getPatternFromOrderNumber(orderN).getStep(stepN); - for (int i = 0; i < 4; ++i) { - Effect&& eff = Effect::makeEffectData(attrib.source, step.getEffectID(i), step.getEffectValue(i)); - switch (eff.type) { - case EffectType::PositionJump: - if (eff.value <= lastOrder) { - endOrder = eff.value; - endStep = 0; - } - break; - case EffectType::SongEnd: - endOrder = -1; - endStep = -1; - break; - case EffectType::PatternBreak: - if (orderN == lastOrder - && eff.value < static_cast(getPatternSizeFromOrderNumber(songNum, 0))) { - endOrder = 0; - endStep = eff.value; - } - else if (eff.value < static_cast(getPatternSizeFromOrderNumber(songNum, orderN + 1))) { - endOrder = orderN + 1; - endStep = eff.value; - } - break; - default: - break; - } - } - } - - orderStepMap[orderN] = stepCnt; - stepCnt += song.getPatternSizeFromOrderNumber(orderN); - orderN = endOrder; - } while (orderN != -1 && !orderStepMap.count(orderN)); // stopped song or jumped to played order - - if (orderN == -1) { - nIntroStep = stepCnt; - nLoopStep = 0; - } - else { - nIntroStep = orderStepMap[orderN]; - nLoopStep = stepCnt - orderStepMap[orderN]; - } -} - /********** Real chip interface **********/ void BambooTracker::useSCCI(scci::SoundInterfaceManager* manager) { @@ -1782,7 +1942,7 @@ io::ModuleIO::getInstance().saveModule(container, mod_, instMan_); } -void BambooTracker::setModulePath(std::string path) +void BambooTracker::setModulePath(const std::string& path) { mod_->setFilePath(path); } @@ -1792,7 +1952,7 @@ return mod_->getFilePath(); } -void BambooTracker::setModuleTitle(std::string title) +void BambooTracker::setModuleTitle(const std::string& title) { mod_->setTitle(title); } @@ -1802,7 +1962,7 @@ return mod_->getTitle(); } -void BambooTracker::setModuleAuthor(std::string author) +void BambooTracker::setModuleAuthor(const std::string& author) { mod_->setAuthor(author); } @@ -1812,7 +1972,7 @@ return mod_->getAuthor(); } -void BambooTracker::setModuleCopyright(std::string copyright) +void BambooTracker::setModuleCopyright(const std::string& copyright) { mod_->setCopyright(copyright); } @@ -1822,7 +1982,7 @@ return mod_->getCopyright(); } -void BambooTracker::setModuleComment(std::string comment) +void BambooTracker::setModuleComment(const std::string& comment) { mod_->setComment(comment); } @@ -1898,19 +2058,19 @@ return mod_->getGrooveCount(); } -void BambooTracker::setGroove(int num, std::vector seq) +void BambooTracker::setGroove(int num, const std::vector& seq) { - mod_->setGroove(num, std::move(seq)); + mod_->setGroove(num, seq); } -void BambooTracker::setGrooves(std::vector> seqs) +void BambooTracker::setGrooves(const std::vector>& seqs) { - mod_->setGrooves(std::move(seqs)); + mod_->setGrooves(seqs); } std::vector BambooTracker::getGroove(int num) const { - return mod_->getGroove(num).getSequence(); + return mod_->getGroove(num); } void BambooTracker::clearUnusedPatterns() @@ -1918,16 +2078,11 @@ mod_->clearUnusedPatterns(); } -void BambooTracker::replaceDuplicateInstrumentsInPatterns(std::vector> list) +std::unordered_map BambooTracker::replaceDuplicateInstrumentsInPatterns() { - std::unordered_map map; - for (auto& group : list) { - for (size_t i = 1; i < group.size(); ++i) { - map.emplace(group[i], group.front()); - } - } - + std::unordered_map map = instMan_->getDuplicateInstrumentMap(); mod_->replaceDuplicateInstrumentsInPatterns(map); + return map; } void BambooTracker::clearUnusedADPCMSamples() @@ -1936,7 +2091,7 @@ } /*----- Song -----*/ -void BambooTracker::setSongTitle(int songNum, std::string title) +void BambooTracker::setSongTitle(int songNum, const std::string& title) { mod_->getSong(songNum).setTitle(title); } @@ -1960,7 +2115,7 @@ void BambooTracker::setSongGroove(int songNum, int groove) { mod_->getSong(songNum).setGroove(groove); - tickCounter_->setGroove(mod_->getGroove(groove).getSequence()); + tickCounter_->setGroove(mod_->getGroove(groove)); } int BambooTracker::getSongGroove(int songNum) const @@ -2006,27 +2161,17 @@ return mod_->getSongCount(); } -void BambooTracker::addSong(SongType songType, std::string title) +void BambooTracker::addSong(SongType songType, const std::string& title) { mod_->addSong(songType, title); } -void BambooTracker::sortSongs(std::vector numbers) +void BambooTracker::sortSongs(const std::vector& numbers) { mod_->sortSongs(std::move(numbers)); } -size_t BambooTracker::getAllStepCount(int songNum, size_t loopCnt) const -{ - size_t introSize = 0; - size_t loopSize = 0; - int dummy1 = 0; - int dummy2 = 0; - checkNextPositionOfLastStepAndStepSize(songNum, dummy1, dummy2, introSize, loopSize); - return introSize + loopSize * loopCnt; -} - -void BambooTracker::transposeSong(int songNum, int seminotes, std::vector excludeInsts) +void BambooTracker::transposeSong(int songNum, int seminotes, const std::vector& excludeInsts) { mod_->getSong(songNum).transpose(seminotes, excludeInsts); } @@ -2036,19 +2181,27 @@ mod_->getSong(songNum).swapTracks(track1, track2); } -double BambooTracker::calculateSongLength(int songNum) const +double BambooTracker::getApproximateSongLength(int songNum) const { - SongLengthCalculator calculator(*mod_.get(), songNum); - return calculator.calculateBySecond(); + SongLengthCalculator calc(*mod_.get(), songNum); + return calc.approximateLengthBySecond(); +} + +size_t BambooTracker::getTotalStepCount(int songNum, size_t loopCnt) const +{ + size_t introSize, loopSize; + SongLengthCalculator calc(*mod_.get(), songNum); + calc.totalStepCount(introSize, loopSize); + return introSize + loopSize * loopCnt; } /*----- Bookmark -----*/ -void BambooTracker::addBookmark(int songNum, std::string name, int order, int step) +void BambooTracker::addBookmark(int songNum, const std::string& name, int order, int step) { mod_->getSong(songNum).addBookmark(name, order, step); } -void BambooTracker::changeBookmark(int songNum, int i, std::string name, int order, int step) +void BambooTracker::changeBookmark(int songNum, int i, const std::string& name, int order, int step) { mod_->getSong(songNum).changeBookmark(i, name, order, step); } @@ -2114,7 +2267,7 @@ } /*----- Order -----*/ -std::vector BambooTracker::getOrderData(int songNum, int orderNum) const +std::vector BambooTracker::getOrderData(int songNum, int orderNum) const { return mod_->getSong(songNum).getOrderData(orderNum); } @@ -2135,22 +2288,14 @@ } void BambooTracker::pasteOrderCells(int songNum, int beginTrack, int beginOrder, - std::vector> cells) + const std::vector>& cells) { // Arrange data - std::vector> d; - size_t w = songStyle_.trackAttribs.size() - static_cast(beginTrack); - size_t h = getOrderSize(songNum) - static_cast(beginOrder); - - size_t width = std::min(cells.at(0).size(), w); - size_t height = std::min(cells.size(), h); + size_t w = std::min(cells[0].size(), songStyle_.trackAttribs.size() - static_cast(beginTrack)); + size_t h = std::min(cells.size(), getOrderSize(songNum) - static_cast(beginOrder)); - for (size_t i = 0; i < height; ++i) { - d.emplace_back(); - for (size_t j = 0; j < width; ++j) { - d.at(i).push_back(cells.at(i).at(j)); - } - } + std::vector> d(h); + for (size_t i = 0; i < h; ++i) d[i].assign(cells[i].begin(), cells[i].begin() + w); comMan_.invoke(std::make_unique(mod_, songNum, beginTrack, beginOrder, std::move(d))); } @@ -2192,9 +2337,9 @@ .getStep(stepNum).getNoteNumber(); } -void BambooTracker::setStepNote(int songNum, int trackNum, int orderNum, int stepNum, int octave, Note note, bool instMask, bool volMask) +void BambooTracker::setStepNote(int songNum, int trackNum, int orderNum, int stepNum, const Note& note, bool instMask, bool volMask) { - int nn = octaveAndNoteToNoteNumber(octave, note); + int nn = Note(note).getNoteNumber(); SoundSource src = songStyle_.trackAttribs.at(static_cast(trackNum)).source; int in = -1; @@ -2250,7 +2395,7 @@ && songStyle_.trackAttribs.at(static_cast(trackNum)).source == SoundSource::FM); comMan_.invoke(std::make_unique(mod_, songNum, trackNum, orderNum, stepNum, volume, fmReversed, secondEntry)); curVolume_ = mod_->getSong(songNum).getTrack(trackNum).getPatternFromOrderNumber(orderNum).getStep(stepNum).getVolume(); - if (fmReversed && curVolume_ < 0x80) curVolume_ = 0x7f - curVolume_; + if (fmReversed) curVolume_ = effect_utils::reverseFmVolume(curVolume_); return curVolume_; } @@ -2262,10 +2407,10 @@ std::string BambooTracker::getStepEffectID(int songNum, int trackNum, int orderNum, int stepNum, int n) const { return mod_->getSong(songNum).getTrack(trackNum).getPatternFromOrderNumber(orderNum) - .getStep(stepNum).getEffectID(n); + .getStep(stepNum).getEffectId(n); } -void BambooTracker::setStepEffectIDCharacter(int songNum, int trackNum, int orderNum, int stepNum, int n, std::string id, bool fillValue00, bool secondEntry) +void BambooTracker::setStepEffectIDCharacter(int songNum, int trackNum, int orderNum, int stepNum, int n, const std::string& id, bool fillValue00, bool secondEntry) { comMan_.invoke(std::make_unique(mod_, songNum, trackNum, orderNum, stepNum, n, id, fillValue00, secondEntry)); } @@ -2301,67 +2446,63 @@ comMan_.invoke(std::make_unique(mod_, songNum, trackNum, orderNum, stepNum)); } -void BambooTracker::pastePatternCells(int songNum, int beginTrack, int beginColmn, int beginOrder, int beginStep, - std::vector> cells) +namespace +{ +std::vector> arrangePatternDataCells( + size_t trackCnt, size_t ptnSize, int beginTrack, int beginColmn, int beginStep, + const std::vector>& cells) { - std::vector> d - = arrangePatternDataCells(songNum, beginTrack, beginColmn, beginOrder, beginStep, std::move(cells)); + size_t w = (trackCnt - static_cast(beginTrack) - 1) * 11 + + (11 - static_cast(beginColmn)); + size_t h = ptnSize - static_cast(beginStep); + + size_t width = std::min(cells.at(0).size(), w); + size_t height = std::min(cells.size(), h); + std::vector> d(height); + for (size_t i = 0; i < height; ++i) { + d[i].assign(cells[i].begin(), cells[i].begin() + width); + } + return d; +} +} + +void BambooTracker::pastePatternCells(int songNum, int beginTrack, int beginColmn, int beginOrder, int beginStep, + const std::vector>& cells) +{ + auto d = arrangePatternDataCells(songStyle_.trackAttribs.size(), getPatternSizeFromOrderNumber(songNum, beginOrder), + beginTrack, beginColmn, beginStep, cells); comMan_.invoke(std::make_unique( mod_, songNum, beginTrack, beginColmn, beginOrder, beginStep, std::move(d))); } void BambooTracker::pasteMixPatternCells(int songNum, int beginTrack, int beginColmn, int beginOrder, int beginStep, - std::vector> cells) + const std::vector >& cells) { - std::vector> d - = arrangePatternDataCells(songNum, beginTrack, beginColmn, beginOrder, beginStep, std::move(cells)); - + auto d = arrangePatternDataCells(songStyle_.trackAttribs.size(), getPatternSizeFromOrderNumber(songNum, beginOrder), + beginTrack, beginColmn, beginStep, cells); comMan_.invoke(std::make_unique( mod_, songNum, beginTrack, beginColmn, beginOrder, beginStep, std::move(d))); } void BambooTracker::pasteOverwritePatternCells(int songNum, int beginTrack, int beginColmn, int beginOrder, - int beginStep, std::vector> cells) + int beginStep, const std::vector >& cells) { - std::vector> d - = arrangePatternDataCells(songNum, beginTrack, beginColmn, beginOrder, beginStep, std::move(cells)); - + auto d = arrangePatternDataCells(songStyle_.trackAttribs.size(), getPatternSizeFromOrderNumber(songNum, beginOrder), + beginTrack, beginColmn, beginStep, cells); comMan_.invoke(std::make_unique( mod_, songNum, beginTrack, beginColmn, beginOrder, beginStep, std::move(d))); } void BambooTracker::pasteInsertPatternCells(int songNum, int beginTrack, int beginColmn, int beginOrder, - int beginStep, std::vector> cells) + int beginStep, const std::vector >& cells) { - std::vector> d - = arrangePatternDataCells(songNum, beginTrack, beginColmn, beginOrder, beginStep, std::move(cells)); - + auto d = arrangePatternDataCells(songStyle_.trackAttribs.size(), getPatternSizeFromOrderNumber(songNum, beginOrder), + beginTrack, beginColmn, beginStep, cells); comMan_.invoke(std::make_unique( mod_, songNum, beginTrack, beginColmn, beginOrder, beginStep, std::move(d))); } -std::vector> BambooTracker::arrangePatternDataCells(int songNum, int beginTrack, int beginColmn, int beginOrder, int beginStep, - std::vector> cells) -{ - std::vector> d; - size_t w = (songStyle_.trackAttribs.size() - static_cast(beginTrack) - 1) * 11 - + (11 - static_cast(beginColmn)); - size_t h = getPatternSizeFromOrderNumber(songNum, beginOrder) - static_cast(beginStep); - - size_t width = std::min(cells.at(0).size(), w); - size_t height = std::min(cells.size(), h); - - for (size_t i = 0; i < height; ++i) { - d.emplace_back(); - for (size_t j = 0; j < width; ++j) { - d.at(i).push_back(cells.at(i).at(j)); - } - } - - return d; -} - void BambooTracker::erasePatternCells(int songNum, int beginTrack, int beginColmn, int beginOrder, int beginStep, int endTrack, int endColmn, int endStep) { diff -Nru bambootracker-0.4.5+git20121209/BambooTracker/bamboo_tracker_defs.hpp bambootracker-0.4.6/BambooTracker/bamboo_tracker_defs.hpp --- bambootracker-0.4.5+git20121209/BambooTracker/bamboo_tracker_defs.hpp 1970-01-01 00:00:00.000000000 +0000 +++ bambootracker-0.4.6/BambooTracker/bamboo_tracker_defs.hpp 2021-02-11 14:37:17.000000000 +0000 @@ -0,0 +1,44 @@ +/* + * Copyright (C) 2018-2021 Rerrah + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +#pragma once + +enum class SoundSource : int +{ + FM = 1, + SSG = 2, + RHYTHM = 4, + ADPCM = 8 +}; + +namespace bt_defs +{ +constexpr int OUTPUT_HISTORY_SIZE = 1024; + +constexpr int NSTEP_FM_VOLUME = 0x80; +constexpr int NSTEP_SSG_VOLUME = 0x10; +constexpr int NSTEP_RHYTHM_VOLUME = 0x20; +constexpr int NSTEP_ADPCM_VOLUME = 0x100; +} diff -Nru bambootracker-0.4.5+git20121209/BambooTracker/bamboo_tracker.hpp bambootracker-0.4.6/BambooTracker/bamboo_tracker.hpp --- bambootracker-0.4.5+git20121209/BambooTracker/bamboo_tracker.hpp 2020-12-08 14:05:28.000000000 +0000 +++ bambootracker-0.4.6/BambooTracker/bamboo_tracker.hpp 2021-02-11 14:37:17.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (C) 2018-2020 Rerrah + * Copyright (C) 2018-2021 Rerrah * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation @@ -31,31 +31,33 @@ #include #include #include -#include "configuration.hpp" -#include "opna_controller.hpp" +#include +#include #include "jamming.hpp" -#include "instruments_manager.hpp" #include "instrument.hpp" -#include "tick_counter.hpp" #include "module.hpp" -#include "song.hpp" #include "command/command_manager.hpp" -#include "chip/scci/scci.hpp" #include "chip/c86ctl/c86ctl_wrapper.hpp" -#include "effect.hpp" -#include "playback.hpp" +#include "chip/scci/scci.hpp" #include "io/binary_container.hpp" -#include "io/wav_container.hpp" #include "io/export_io.hpp" +#include "io/wav_container.hpp" +#include "bamboo_tracker_defs.hpp" #include "enum_hash.hpp" -#include "misc.hpp" +class Configuration; +enum class EffectDisplayControl; +enum class RealChipInterface; class AbstractBank; +class OPNAController; +class PlaybackManager; +class TickCounter; class BambooTracker { public: explicit BambooTracker(std::weak_ptr config); + ~BambooTracker(); // Change confuguration void changeConfiguration(std::weak_ptr config); @@ -77,114 +79,137 @@ int getCurrentInstrumentNumber() const; // Instrument edit - void addInstrument(int num, InstrumentType type, std::string name); + void addInstrument(int num, InstrumentType type, const std::string& name); void removeInstrument(int num); std::unique_ptr getInstrument(int num); void cloneInstrument(int num, int refNum); void deepCloneInstrument(int num, int refNum); void swapInstruments(int a, int b, bool patternChange); - void loadInstrument(io::BinaryContainer& container, std::string path, int instNum); + void loadInstrument(io::BinaryContainer& container, const std::string& path, int instNum); void saveInstrument(io::BinaryContainer& container, int instNum); void importInstrument(const AbstractBank &bank, size_t index, int instNum); - void exportInstruments(io::BinaryContainer& container, std::vector instNums); + void exportInstruments(io::BinaryContainer& container, const std::vector& instNums); int findFirstFreeInstrumentNumber() const; - void setInstrumentName(int num, std::string name); + void setInstrumentName(int num, const std::string& name); void clearAllInstrument(); std::vector getInstrumentIndices() const; std::vector getUnusedInstrumentIndices() const; void clearUnusedInstrumentProperties(); std::vector getInstrumentNames() const; - std::vector> checkDuplicateInstruments() const; //--- FM void setEnvelopeFMParameter(int envNum, FMEnvelopeParameter param, int value); void setEnvelopeFMOperatorEnable(int envNum, int opNum, bool enable); void setInstrumentFMEnvelope(int instNum, int envNum); - std::vector getEnvelopeFMUsers(int envNum) const; + std::multiset getEnvelopeFMUsers(int envNum) const; void setLFOFMParameter(int lfoNum, FMLFOParameter param, int value); void setInstrumentFMLFOEnabled(int instNum, bool enabled); void setInstrumentFMLFO(int instNum, int lfoNum); - std::vector getLFOFMUsers(int lfoNum) const; + std::multiset getLFOFMUsers(int lfoNum) const; - void addOperatorSequenceFMSequenceCommand(FMEnvelopeParameter param, int opSeqNum, int type, int data); - void removeOperatorSequenceFMSequenceCommand(FMEnvelopeParameter param, int opSeqNum); - void setOperatorSequenceFMSequenceCommand(FMEnvelopeParameter param, int opSeqNum, int cnt, int type, int data); - void setOperatorSequenceFMLoops(FMEnvelopeParameter param, int opSeqNum, std::vector begins, std::vector ends, std::vector times); - void setOperatorSequenceFMRelease(FMEnvelopeParameter param, int opSeqNum, ReleaseType type, int begin); + void addOperatorSequenceFMSequenceData(FMEnvelopeParameter param, int opSeqNum, int data); + void removeOperatorSequenceFMSequenceData(FMEnvelopeParameter param, int opSeqNum); + void setOperatorSequenceFMSequenceData(FMEnvelopeParameter param, int opSeqNum, int cnt, int data); + void addOperatorSequenceFMLoop(FMEnvelopeParameter param, int opSeqNum, const InstrumentSequenceLoop& loop); + void removeOperatorSequenceFMLoop(FMEnvelopeParameter param, int opSeqNum, int begin, int end); + void changeOperatorSequenceFMLoop(FMEnvelopeParameter param, int opSeqNum, int prevBegin, int prevEnd, const InstrumentSequenceLoop& loop); + void clearOperatorSequenceFMLoops(FMEnvelopeParameter param, int opSeqNum); + void setOperatorSequenceFMRelease(FMEnvelopeParameter param, int opSeqNum, const InstrumentSequenceRelease& release); void setInstrumentFMOperatorSequence(int instNum, FMEnvelopeParameter param, int opSeqNum); void setInstrumentFMOperatorSequenceEnabled(int instNum, FMEnvelopeParameter param, bool enabled); - std::vector getOperatorSequenceFMUsers(FMEnvelopeParameter param, int opSeqNum) const; + std::multiset getOperatorSequenceFMUsers(FMEnvelopeParameter param, int opSeqNum) const; void setArpeggioFMType(int arpNum, SequenceType type); - void addArpeggioFMSequenceCommand(int arpNum, int type, int data); - void removeArpeggioFMSequenceCommand(int arpNum); - void setArpeggioFMSequenceCommand(int arpNum, int cnt, int type, int data); - void setArpeggioFMLoops(int arpNum, std::vector begins, std::vector ends, std::vector times); - void setArpeggioFMRelease(int arpNum, ReleaseType type, int begin); + void addArpeggioFMSequenceData(int arpNum, int data); + void removeArpeggioFMSequenceData(int arpNum); + void setArpeggioFMSequenceData(int arpNum, int cnt, int data); + void addArpeggioFMLoop(int arpNum, const InstrumentSequenceLoop& loop); + void removeArpeggioFMLoop(int arpNum, int begin, int end); + void changeArpeggioFMLoop(int arpNum, int prevBegin, int prevEnd, const InstrumentSequenceLoop& loop); + void clearArpeggioFMLoops(int arpNum); + void setArpeggioFMRelease(int arpNum, const InstrumentSequenceRelease& release); void setInstrumentFMArpeggio(int instNum, FMOperatorType op, int arpNum); void setInstrumentFMArpeggioEnabled(int instNum, FMOperatorType op, bool enabled); - std::vector getArpeggioFMUsers(int arpNum) const; + std::multiset getArpeggioFMUsers(int arpNum) const; void setPitchFMType(int ptNum, SequenceType type); - void addPitchFMSequenceCommand(int ptNum, int type, int data); - void removePitchFMSequenceCommand(int ptNum); - void setPitchFMSequenceCommand(int ptNum, int cnt, int type, int data); - void setPitchFMLoops(int ptNum, std::vector begins, std::vector ends, std::vector times); - void setPitchFMRelease(int ptNum, ReleaseType type, int begin); + void addPitchFMSequenceData(int ptNum, int data); + void removePitchFMSequenceData(int ptNum); + void setPitchFMSequenceData(int ptNum, int cnt, int data); + void addPitchFMLoop(int ptNum, const InstrumentSequenceLoop& loop); + void removePitchFMLoop(int ptNum, int begin, int end); + void changePitchFMLoop(int ptNum, int prevBegin, int prevEnd, const InstrumentSequenceLoop& loop); + void clearPitchFMLoops(int ptNum); + void setPitchFMRelease(int ptNum, const InstrumentSequenceRelease& release); void setInstrumentFMPitch(int instNum, FMOperatorType op, int ptNum); void setInstrumentFMPitchEnabled(int instNum, FMOperatorType op, bool enabled); - std::vector getPitchFMUsers(int ptNum) const; + std::multiset getPitchFMUsers(int ptNum) const; void setInstrumentFMEnvelopeResetEnabled(int instNum, FMOperatorType op, bool enabled); //--- SSG - void addWaveformSSGSequenceCommand(int wfNum, int type, int data); - void removeWaveformSSGSequenceCommand(int wfNum); - void setWaveformSSGSequenceCommand(int wfNum, int cnt, int type, int data); - void setWaveformSSGLoops(int wfNum, std::vector begins, std::vector ends, std::vector times); - void setWaveformSSGRelease(int wfNum, ReleaseType type, int begin); + void addWaveformSSGSequenceData(int wfNum, const SSGWaveformUnit& data); + void removeWaveformSSGSequenceData(int wfNum); + void setWaveformSSGSequenceData(int wfNum, int cnt, const SSGWaveformUnit& data); + void addWaveformSSGLoop(int wfNum, const InstrumentSequenceLoop& loop); + void removeWaveformSSGLoop(int wfNum, int begin, int end); + void changeWaveformSSGLoop(int wfNum, int prevBegin, int prevEnd, const InstrumentSequenceLoop& loop); + void clearWaveformSSGLoops(int wfNum); + void setWaveformSSGRelease(int wfNum, const InstrumentSequenceRelease& release); void setInstrumentSSGWaveform(int instNum, int wfNum); void setInstrumentSSGWaveformEnabled(int instNum, bool enabled); - std::vector getWaveformSSGUsers(int wfNum) const; + std::multiset getWaveformSSGUsers(int wfNum) const; - void addToneNoiseSSGSequenceCommand(int tnNum, int type, int data); - void removeToneNoiseSSGSequenceCommand(int tnNum); - void setToneNoiseSSGSequenceCommand(int tnNum, int cnt, int type, int data); - void setToneNoiseSSGLoops(int tnNum, std::vector begins, std::vector ends, std::vector times); - void setToneNoiseSSGRelease(int tnNum, ReleaseType type, int begin); + void addToneNoiseSSGSequenceData(int tnNum, int data); + void removeToneNoiseSSGSequenceData(int tnNum); + void setToneNoiseSSGSequenceData(int tnNum, int cnt, int data); + void addToneNoiseSSGLoop(int tnNum, const InstrumentSequenceLoop& loop); + void removeToneNoiseSSGLoop(int tnNum, int begin, int end); + void changeToneNoiseSSGLoop(int tnNum, int prevBegin, int prevEnd, const InstrumentSequenceLoop& loop); + void clearToneNoiseSSGLoops(int tnNum); + void setToneNoiseSSGRelease(int tnNum, const InstrumentSequenceRelease& release); void setInstrumentSSGToneNoise(int instNum, int tnNum); void setInstrumentSSGToneNoiseEnabled(int instNum, bool enabled); - std::vector getToneNoiseSSGUsers(int tnNum) const; + std::multiset getToneNoiseSSGUsers(int tnNum) const; - void addEnvelopeSSGSequenceCommand(int envNum, int type, int data); - void removeEnvelopeSSGSequenceCommand(int envNum); - void setEnvelopeSSGSequenceCommand(int envNum, int cnt, int type, int data); - void setEnvelopeSSGLoops(int envNum, std::vector begins, std::vector ends, std::vector times); - void setEnvelopeSSGRelease(int envNum, ReleaseType type, int begin); + void addEnvelopeSSGSequenceData(int envNum, const SSGEnvelopeUnit& data); + void removeEnvelopeSSGSequenceData(int envNum); + void setEnvelopeSSGSequenceData(int envNum, int cnt, const SSGEnvelopeUnit& data); + void addEnvelopeSSGLoop(int envNum, const InstrumentSequenceLoop& loop); + void removeEnvelopeSSGLoop(int envNum, int begin, int end); + void changeEnvelopeSSGLoop(int envNum, int prevBegin, int prevEnd, const InstrumentSequenceLoop& loop); + void clearEnvelopeSSGLoops(int envNum); + void setEnvelopeSSGRelease(int envNum, const InstrumentSequenceRelease& release); void setInstrumentSSGEnvelope(int instNum, int envNum); void setInstrumentSSGEnvelopeEnabled(int instNum, bool enabled); - std::vector getEnvelopeSSGUsers(int envNum) const; + std::multiset getEnvelopeSSGUsers(int envNum) const; void setArpeggioSSGType(int arpNum, SequenceType type); - void addArpeggioSSGSequenceCommand(int arpNum, int type, int data); - void removeArpeggioSSGSequenceCommand(int arpNum); - void setArpeggioSSGSequenceCommand(int arpNum, int cnt, int type, int data); - void setArpeggioSSGLoops(int arpNum, std::vector begins, std::vector ends, std::vector times); - void setArpeggioSSGRelease(int arpNum, ReleaseType type, int begin); + void addArpeggioSSGSequenceData(int arpNum, int data); + void removeArpeggioSSGSequenceData(int arpNum); + void setArpeggioSSGSequenceData(int arpNum, int cnt, int data); + void addArpeggioSSGLoop(int arpNum, const InstrumentSequenceLoop& loop); + void removeArpeggioSSGLoop(int arpNum, int begin, int end); + void changeArpeggioSSGLoop(int arpNum, int prevBegin, int prevEnd, const InstrumentSequenceLoop& loop); + void clearArpeggioSSGLoops(int arpNum); + void setArpeggioSSGRelease(int arpNum, const InstrumentSequenceRelease& release); void setInstrumentSSGArpeggio(int instNum, int arpNum); void setInstrumentSSGArpeggioEnabled(int instNum, bool enabled); - std::vector getArpeggioSSGUsers(int arpNum) const; + std::multiset getArpeggioSSGUsers(int arpNum) const; void setPitchSSGType(int ptNum, SequenceType type); - void addPitchSSGSequenceCommand(int ptNum, int type, int data); - void removePitchSSGSequenceCommand(int ptNum); - void setPitchSSGSequenceCommand(int ptNum, int cnt, int type, int data); - void setPitchSSGLoops(int ptNum, std::vector begins, std::vector ends, std::vector times); - void setPitchSSGRelease(int ptNum, ReleaseType type, int begin); + void addPitchSSGSequenceData(int ptNum, int data); + void removePitchSSGSequenceData(int ptNum); + void setPitchSSGSequenceData(int ptNum, int cnt, int data); + void addPitchSSGLoop(int ptNum, const InstrumentSequenceLoop& loop); + void removePitchSSGLoop(int ptNum, int begin, int end); + void changePitchSSGLoop(int ptNum, int prevBegin, int prevEnd, const InstrumentSequenceLoop& loop); + void clearPitchSSGLoops(int ptNum); + void setPitchSSGRelease(int ptNum, const InstrumentSequenceRelease& release); void setInstrumentSSGPitch(int instNum, int ptNum); void setInstrumentSSGPitchEnabled(int instNum, bool enabled); - std::vector getPitchSSGUsers(int ptNum) const; + std::multiset getPitchSSGUsers(int ptNum) const; //--- ADPCM size_t getADPCMLimit() const; @@ -196,43 +221,53 @@ int getSampleADPCMRootDeltaN(int sampNum) const; void setSampleADPCMRepeatEnabled(int sampNum, bool enabled); bool getSampleADPCMRepeatEnabled(int sampNum) const; - void storeSampleADPCMRawSample(int sampNum, std::vector sample); + void storeSampleADPCMRawSample(int sampNum, const std::vector& sample); + void storeSampleADPCMRawSample(int sampNum, std::vector&& sample); std::vector getSampleADPCMRawSample(int sampNum) const; void clearSampleADPCMRawSample(int sampNum); - void assignSampleADPCMRawSamples(); + bool assignSampleADPCMRawSamples(); size_t getSampleADPCMStartAddress(int sampNum) const; size_t getSampleADPCMStopAddress(int sampNum) const; void setInstrumentADPCMSample(int instNum, int sampNum); - std::vector getSampleADPCMUsers(int sampNum) const; + std::multiset getSampleADPCMUsers(int sampNum) const; - void addEnvelopeADPCMSequenceCommand(int envNum, int type, int data); - void removeEnvelopeADPCMSequenceCommand(int envNum); - void setEnvelopeADPCMSequenceCommand(int envNum, int cnt, int type, int data); - void setEnvelopeADPCMLoops(int envNum, std::vector begins, std::vector ends, std::vector times); - void setEnvelopeADPCMRelease(int envNum, ReleaseType type, int begin); + void addEnvelopeADPCMSequenceData(int envNum, int data); + void removeEnvelopeADPCMSequenceData(int envNum); + void setEnvelopeADPCMSequenceData(int envNum, int cnt, int data); + void addEnvelopeADPCMLoop(int arpNum, const InstrumentSequenceLoop& loop); + void removeEnvelopeADPCMLoop(int envNum, int begin, int end); + void changeEnvelopeADPCMLoop(int envNum, int prevBegin, int prevEnd, const InstrumentSequenceLoop& loop); + void clearEnvelopeADPCMLoops(int envNum); + void setEnvelopeADPCMRelease(int arpNum, const InstrumentSequenceRelease& release); void setInstrumentADPCMEnvelope(int instNum, int envNum); void setInstrumentADPCMEnvelopeEnabled(int instNum, bool enabled); - std::vector getEnvelopeADPCMUsers(int envNum) const; + std::multiset getEnvelopeADPCMUsers(int envNum) const; void setArpeggioADPCMType(int arpNum, SequenceType type); - void addArpeggioADPCMSequenceCommand(int arpNum, int type, int data); - void removeArpeggioADPCMSequenceCommand(int arpNum); - void setArpeggioADPCMSequenceCommand(int arpNum, int cnt, int type, int data); - void setArpeggioADPCMLoops(int arpNum, std::vector begins, std::vector ends, std::vector times); - void setArpeggioADPCMRelease(int arpNum, ReleaseType type, int begin); + void addArpeggioADPCMSequenceData(int arpNum, int data); + void removeArpeggioADPCMSequenceData(int arpNum); + void setArpeggioADPCMSequenceData(int arpNum, int cnt, int data); + void addArpeggioADPCMLoop(int arpNum, const InstrumentSequenceLoop& loop); + void removeArpeggioADPCMLoop(int arpNum, int begin, int end); + void changeArpeggioADPCMLoop(int arpNum, int prevBegin, int prevEnd, const InstrumentSequenceLoop& loop); + void clearArpeggioADPCMLoops(int arpNum); + void setArpeggioADPCMRelease(int arpNum, const InstrumentSequenceRelease& release); void setInstrumentADPCMArpeggio(int instNum, int arpNum); void setInstrumentADPCMArpeggioEnabled(int instNum, bool enabled); - std::vector getArpeggioADPCMUsers(int arpNum) const; + std::multiset getArpeggioADPCMUsers(int arpNum) const; void setPitchADPCMType(int ptNum, SequenceType type); - void addPitchADPCMSequenceCommand(int ptNum, int type, int data); - void removePitchADPCMSequenceCommand(int ptNum); - void setPitchADPCMSequenceCommand(int ptNum, int cnt, int type, int data); - void setPitchADPCMLoops(int ptNum, std::vector begins, std::vector ends, std::vector times); - void setPitchADPCMRelease(int ptNum, ReleaseType type, int begin); + void addPitchADPCMSequenceData(int ptNum, int data); + void removePitchADPCMSequenceData(int ptNum); + void setPitchADPCMSequenceData(int ptNum, int cnt, int data); + void addPitchADPCMLoop(int ptNum, const InstrumentSequenceLoop& loop); + void removePitchADPCMLoop(int ptNum, int begin, int end); + void changePitchADPCMLoop(int ptNum, int prevBegin, int prevEnd, const InstrumentSequenceLoop& loop); + void clearPitchADPCMLoops(int ptNum); + void setPitchADPCMRelease(int ptNum, const InstrumentSequenceRelease& release); void setInstrumentADPCMPitch(int instNum, int ptNum); void setInstrumentADPCMPitchEnabled(int instNum, bool enabled); - std::vector getPitchADPCMUsers(int ptNum) const; + std::multiset getPitchADPCMUsers(int ptNum) const; //--- Drumkit void setInstrumentDrumkitSample(int instNum, int key, int sampNum); @@ -267,7 +302,8 @@ void jamKeyOnForced(int keyNum, SoundSource src, bool volumeSet, std::shared_ptr inst = nullptr); void jamKeyOffForced(JamKey key, SoundSource src); void jamKeyOffForced(int keyNum, SoundSource src); - std::vector> assignADPCMBeforeForcedJamKeyOn(std::shared_ptr inst); + bool assignADPCMBeforeForcedJamKeyOn(std::shared_ptr inst, + std::unordered_map>& sampAddrs); // Play song void startPlaySong(); @@ -289,11 +325,12 @@ int getMarkerStep() const; // Export - bool exportToWav(io::WavContainer& container, int loopCnt, std::function bar); + using ExportCancellCallback = std::function; + bool exportToWav(io::WavContainer& container, int loopCnt, ExportCancellCallback checkFunc); bool exportToVgm(io::BinaryContainer& container, int target, bool gd3TagEnabled, - io::GD3Tag tag, std::function bar); - bool exportToS98(io::BinaryContainer& container, int target, bool tagEnabled, io::S98Tag tag, - int rate, std::function bar); + const io::GD3Tag& tag, ExportCancellCallback checkFunc); + bool exportToS98(io::BinaryContainer& container, int target, bool tagEnabled, + const io::S98Tag& tag, int rate, ExportCancellCallback checkFunc); // Real chip interface void useSCCI(scci::SoundInterfaceManager* manager); @@ -325,15 +362,15 @@ void makeNewModule(); void loadModule(io::BinaryContainer& container); void saveModule(io::BinaryContainer& container); - void setModulePath(std::string path); + void setModulePath(const std::string& path); std::string getModulePath() const; - void setModuleTitle(std::string title); + void setModuleTitle(const std::string& title); std::string getModuleTitle() const; - void setModuleAuthor(std::string author); + void setModuleAuthor(const std::string& author); std::string getModuleAuthor() const; - void setModuleCopyright(std::string copyright); + void setModuleCopyright(const std::string& copyright); std::string getModuleCopyright() const; - void setModuleComment(std::string comment); + void setModuleComment(const std::string& comment); std::string getModuleComment() const; void setModuleTickFrequency(unsigned int freq); unsigned int getModuleTickFrequency() const; @@ -348,14 +385,14 @@ void setModuleCustomMixerSSGLevel(double level); double getModuleCustomMixerSSGLevel() const; size_t getGrooveCount() const; - void setGroove(int num, std::vector seq); - void setGrooves(std::vector> seqs); + void setGroove(int num, const std::vector& seq); + void setGrooves(const std::vector>& seqs); std::vector getGroove(int num) const; void clearUnusedPatterns(); - void replaceDuplicateInstrumentsInPatterns(std::vector> list); + std::unordered_map replaceDuplicateInstrumentsInPatterns(); void clearUnusedADPCMSamples(); /*----- Song -----*/ - void setSongTitle(int songNum, std::string title); + void setSongTitle(int songNum, const std::string& title); std::string getSongTitle(int songNum) const; void setSongTempo(int songNum, int tempo); int getSongTempo(int songNum) const; @@ -368,15 +405,15 @@ void setSongSpeed(int songNum, int speed); int getSongSpeed(int songNum) const; size_t getSongCount() const; - void addSong(SongType songType, std::string title); - void sortSongs(std::vector numbers); - size_t getAllStepCount(int songNum, size_t loopCnt) const; - void transposeSong(int songNum, int seminotes, std::vector excludeInsts); + void addSong(SongType songType, const std::string& title); + void sortSongs(const std::vector& numbers); + void transposeSong(int songNum, int seminotes, const std::vector& excludeInsts); void swapTracks(int songNum, int track1, int track2); - double calculateSongLength(int songNum) const; + double getApproximateSongLength(int songNum) const; + size_t getTotalStepCount(int songNum, size_t loopCnt) const; /*----- Bookmark -----*/ - void addBookmark(int songNum, std::string name, int order, int step); - void changeBookmark(int songNum, int i, std::string name, int order, int step); + void addBookmark(int songNum, const std::string& name, int order, int step); + void changeBookmark(int songNum, int i, const std::string& name, int order, int step); void removeBookmark(int songNum, int i); void clearBookmark(int songNum); void swapBookmarks(int songNum, int a, int b); @@ -391,12 +428,12 @@ void setEffectDisplayWidth(int songNum, int trackNum, size_t w); size_t getEffectDisplayWidth(int songNum, int trackNum) const; /*----- Order -----*/ - std::vector getOrderData(int songNum, int orderNum) const; + std::vector getOrderData(int songNum, int orderNum) const; void setOrderPatternDigit(int songNum, int trackNum, int orderNum, int patternNum, bool secondEntry); void insertOrderBelow(int songNum, int orderNum); void deleteOrder(int songNum, int orderNum); void pasteOrderCells(int songNum, int beginTrack, int beginOrder, - std::vector> cells); + const std::vector>& cells); void duplicateOrder(int songNum, int orderNum); void MoveOrder(int songNum, int orderNum, bool isUp); void clonePatterns(int songNum, int beginOrder, int beginTrack, int endOrder, int endTrack); @@ -405,7 +442,7 @@ bool canAddNewOrder(int songNum) const; /*----- Pattern -----*/ int getStepNoteNumber(int songNum, int trackNum, int orderNum, int stepNum) const; - void setStepNote(int songNum, int trackNum, int orderNum, int stepNum, int octave, Note note, bool instMask, bool volMask); + void setStepNote(int songNum, int trackNum, int orderNum, int stepNum, const Note& note, bool instMask, bool volMask); void setStepKeyOff(int songNum, int trackNum, int orderNum, int stepNum); void setEchoBufferAccess(int songNum, int trackNum, int orderNum, int stepNum, int bufNum); void eraseStepNote(int songNum, int trackNum, int orderNum, int stepNum); @@ -416,7 +453,7 @@ int setStepVolumeDigit(int songNum, int trackNum, int orderNum, int stepNum, int volume, bool secondEntry); void eraseStepVolume(int songNum, int trackNum, int orderNum, int stepNum); std::string getStepEffectID(int songNum, int trackNum, int orderNum, int stepNum, int n) const; - void setStepEffectIDCharacter(int songNum, int trackNum, int orderNum, int stepNum, int n, std::string id, bool fillValue00, bool secondEntry); + void setStepEffectIDCharacter(int songNum, int trackNum, int orderNum, int stepNum, int n, const std::string& id, bool fillValue00, bool secondEntry); int getStepEffectValue(int songNum, int trackNum, int orderNum, int stepNum, int n) const; void setStepEffectValueDigit(int songNum, int trackNum, int orderNum, int stepNum, int n, int value, EffectDisplayControl ctrl, bool secondEntry); void eraseStepEffect(int songNum, int trackNum, int orderNum, int stepNum, int n); @@ -430,15 +467,13 @@ /// 3: effect id /// 4: effect value void pastePatternCells(int songNum, int beginTrack, int beginColmn, int beginOrder, int beginStep, - std::vector> cells); + const std::vector>& cells); void pasteMixPatternCells(int songNum, int beginTrack, int beginColmn, int beginOrder, int beginStep, - std::vector> cells); + const std::vector>& cells); void pasteOverwritePatternCells(int songNum, int beginTrack, int beginColmn, int beginOrder, - int beginStep, std::vector> cells); + int beginStep, const std::vector>& cells); void pasteInsertPatternCells(int songNum, int beginTrack, int beginColmn, int beginOrder, - int beginStep, std::vector> cells); - std::vector> arrangePatternDataCells(int songNum, int beginTrack, int beginColmn, int beginOrder, int beginStep, - std::vector> cells); + int beginStep, const std::vector>& cells); void erasePatternCells(int songNum, int beginTrack, int beginColmn, int beginOrder, int beginStep, int endTrack, int endColmn, int endStep); void transposeNoteInPattern(int songNum, int beginTrack, int beginOrder, int beginStep, @@ -486,8 +521,6 @@ bool isFollowPlay_; bool storeOnlyUsedSamples_; - static const uint32_t CHIP_CLOCK; - // Jam mode void funcJamKeyOn(JamKey key, int keyNum, const TrackAttribute& attrib, bool volumeSet, std::shared_ptr inst = nullptr); @@ -495,7 +528,4 @@ // Play song void startPlay(); - - void checkNextPositionOfLastStepAndStepSize( - int songNum, int& endOrder, int& endStep, size_t& nIntroStep, size_t& nLoopStep) const; }; diff -Nru bambootracker-0.4.5+git20121209/BambooTracker/BambooTracker.pro bambootracker-0.4.6/BambooTracker/BambooTracker.pro --- bambootracker-0.4.5+git20121209/BambooTracker/BambooTracker.pro 2020-12-08 14:05:28.000000000 +0000 +++ bambootracker-0.4.6/BambooTracker/BambooTracker.pro 2021-02-11 14:37:17.000000000 +0000 @@ -39,8 +39,9 @@ CONFIG += c++14 # C/C++ compiler flags +message("Qt is version" $$QT_VERSION) msvc { - message("Qt configured with MSVC") + message("Configured compiler is MSVC") message("Compiler is version" $$QT_MSC_FULL_VER) CPP_WARNING_FLAGS += /Wall /Wp64 /WX CPP_WARNING_FLAGS += /source-charset:utf-8 @@ -53,14 +54,14 @@ # Get the compiler version for version-specific handling clang { defined(QMAKE_APPLE_CLANG_MAJOR_VERSION, var) { - message("Qt configured with Apple LLVM") + message("Configured compiler is Apple LLVM") CONFIG += clang-apple COMPILER_MAJOR_VERSION = $$QT_APPLE_CLANG_MAJOR_VERSION COMPILER_MINOR_VERSION = $$QT_APPLE_CLANG_MINOR_VERSION COMPILER_PATCH_VERSION = $$QT_APPLE_CLANG_PATCH_VERSION } else { - message("Qt configured with LLVM") + message("Configured compiler is LLVM") CONFIG += clang-normal COMPILER_MAJOR_VERSION = $$QT_CLANG_MAJOR_VERSION COMPILER_MINOR_VERSION = $$QT_CLANG_MINOR_VERSION @@ -68,7 +69,7 @@ } } else { - message("Qt configured with GCC") + message("Configured compiler is GCC") COMPILER_MAJOR_VERSION = $$QT_GCC_MAJOR_VERSION COMPILER_MINOR_VERSION = $$QT_GCC_MINOR_VERSION COMPILER_PATCH_VERSION = $$QT_GCC_PATCH_VERSION @@ -76,55 +77,15 @@ COMPILER_VERSION = $${COMPILER_MAJOR_VERSION}.$${COMPILER_MINOR_VERSION}.$${COMPILER_PATCH_VERSION} message("Compiler is version" $$COMPILER_VERSION) - # Temporary known-error downgrades - - # RtAudio code contains VLAs for multiple APIs, needs a stronger patch in the future - CPP_WARNING_FLAGS += -Wno-error=vla - - clang { - # See VLA workaround, needs additional switch on Clang - CPP_WARNING_FLAGS += -Wno-vla-extension - - # macOS 10.14 (LLVM 11.0.0) targeting gnu++1y (C++14) errors when - # using system-installed JACK headers in RtAudio & RtMidi - # /usr/local/Cellar/jack/0.125.0_4/include/jack/types.h:(389,411) - use_jack { - CPP_WARNING_FLAGS += -Wno-deprecated-register - } - - # FreeBSD ALSA headers use zero-length array - # /usr/local/include/alsa/pcm.h:597 - freebsd:use_alsa { - CPP_WARNING_FLAGS += -Wno-zero-length-array - } - - # Definition of implicit copy constructor with user-declared copy assignment operator deprecated - # Problem in Qt5 itself, nothing we can fix about it - # Introduced by LLVM 10, triggered by any reference to QString - # TODO: Extend to clang-apple once LLVM 10 reaches the platform and they decide on a new custom version - # /usr/local/include/qt5/QtCore/(qbytearray.h:586,qstring.h:1191) - clang-normal:greaterThan(COMPILER_MAJOR_VERSION, 9) { - CPP_WARNING_FLAGS += -Wno-deprecated-copy - } - } - else { - # Definition of implicit copy constructor with user-declared copy assignment operator deprecated - # Problem in Qt5 itself, nothing we can fix about it - # Introduced by GCC 9, triggered by any reference to QString - # /usr/local/include/qt5/QtCore/(qbytearray.h:586,qstring.h:1191) - greaterThan(COMPILER_MAJOR_VERSION, 8) { - CPP_WARNING_FLAGS += -Wno-error=deprecated-copy - } - } + # Temporary known-error downgrades here } else { - message("Unknown compiler, won't attempt to add warning & pedantic compiler switches.") + message("Configured compiler is unknown, no attempt to add warning & pedantic compiler switches") } QMAKE_CFLAGS_WARN_ON += $$CPP_WARNING_FLAGS QMAKE_CXXFLAGS_WARN_ON += $$CPP_WARNING_FLAGS SOURCES += \ - calc_pitch.cpp \ chip/c86ctl/c86ctl_wrapper.cpp \ chip/register_write_logger.cpp \ command/instrument/swap_instruments_command.cpp \ @@ -136,9 +97,8 @@ gui/color_palette_handler.cpp \ gui/command/instrument/instrument_command_qt_utils.cpp \ gui/command/instrument/swap_instruments_qt_command.cpp \ - gui/command/pattern/change_values_in_pattern_qt_command.cpp \ - gui/command/pattern/paste_insert_copied_data_to_pattern_qt_command.cpp \ - gui/command/pattern/transpose_note_in_pattern_qt_command.cpp \ + gui/command/order/order_list_common_qt_command.cpp \ + gui/command/pattern/pattern_editor_common_qt_command.cpp \ gui/drop_detect_list_widget.cpp \ gui/effect_description.cpp \ gui/effect_list_dialog.cpp \ @@ -158,7 +118,8 @@ gui/swap_tracks_dialog.cpp \ gui/track_visibility_memory_handler.cpp \ gui/transpose_song_dialog.cpp \ - instrument/waveform_adpcm.cpp \ + instrument/sample_adpcm.cpp \ + instrument/sequence_property.cpp \ io/btb_io.cpp \ io/bti_io.cpp \ io/btm_io.cpp \ @@ -169,8 +130,11 @@ io/ins_io.cpp \ io/io_utils.cpp \ io/opni_io.cpp \ + io/p86_io.cpp \ io/ppc_io.cpp \ + io/pps_io.cpp \ io/pvi_io.cpp \ + io/pzi_io.cpp \ io/tfi_io.cpp \ io/vgi_io.cpp \ io/wav_container.cpp \ @@ -190,6 +154,7 @@ chip/nuked/ym3438.c \ bamboo_tracker.cpp \ module/effect.cpp \ + note.cpp \ playback.cpp \ song_length_calculator.cpp \ audio/audio_stream.cpp \ @@ -222,88 +187,55 @@ gui/pattern_editor/pattern_editor_panel.cpp \ gui/pattern_editor/pattern_editor.cpp \ gui/instrument_editor/instrument_editor_ssg_form.cpp \ - gui/command/pattern/set_key_off_to_step_qt_command.cpp \ command/pattern/set_key_off_to_step_command.cpp \ command/pattern/set_key_on_to_step_command.cpp \ - gui/command/pattern/set_key_on_to_step_qt_command.cpp \ - gui/command/pattern/set_instrument_to_step_qt_command.cpp \ command/pattern/set_instrument_to_step_command.cpp \ - gui/command/pattern/erase_instrument_in_step_qt_command.cpp \ command/pattern/erase_instrument_in_step_command.cpp \ - gui/command/pattern/set_volume_to_step_qt_command.cpp \ command/pattern/set_volume_to_step_command.cpp \ - gui/command/pattern/erase_volume_in_step_qt_command.cpp \ command/pattern/erase_volume_in_step_command.cpp \ command/pattern/set_effect_id_to_step_command.cpp \ - gui/command/pattern/set_effect_id_to_step_qt_command.cpp \ command/pattern/erase_effect_in_step_command.cpp \ - gui/command/pattern/erase_effect_in_step_qt_command.cpp \ command/pattern/set_effect_value_to_step_command.cpp \ - gui/command/pattern/set_effect_value_to_step_qt_command.cpp \ command/pattern/erase_effect_value_in_step_command.cpp \ - gui/command/pattern/erase_effect_value_in_step_qt_command.cpp \ command/pattern/insert_step_command.cpp \ command/pattern/delete_previous_step_command.cpp \ - gui/command/pattern/insert_step_qt_command.cpp \ - gui/command/pattern/delete_previous_step_qt_command.cpp \ - gui/command/pattern/erase_step_qt_command.cpp \ command/pattern/erase_step_command.cpp \ gui/command/instrument/deep_clone_instrument_qt_command.cpp \ command/instrument/deep_clone_instrument_command.cpp \ command/instrument/clone_instrument_command.cpp \ gui/command/instrument/clone_instrument_qt_command.cpp \ command/order/set_pattern_to_order_command.cpp \ - gui/command/order/set_pattern_to_order_qt_command.cpp \ command/order/insert_order_below_command.cpp \ command/order/delete_order_command.cpp \ - gui/command/order/insert_order_below_qt_command.cpp \ - gui/command/order/delete_order_qt_command.cpp \ command/pattern/paste_copied_data_to_pattern_command.cpp \ - gui/command/pattern/paste_copied_data_to_pattern_qt_command.cpp \ command/pattern/erase_cells_in_pattern_command.cpp \ - gui/command/pattern/erase_cells_in_pattern_qt_command.cpp \ command/order/paste_copied_data_to_order_command.cpp \ - gui/command/order/paste_copied_data_to_order_qt_command.cpp \ gui/instrument_editor/instrument_form_manager.cpp \ instrument/lfo_fm.cpp \ gui/instrument_editor/visualized_instrument_macro_editor.cpp \ - instrument/command_sequence.cpp \ instrument/effect_iterator.cpp \ command/pattern/paste_mix_copied_data_to_pattern_command.cpp \ - gui/command/pattern/paste_mix_copied_data_to_pattern_qt_command.cpp \ gui/module_properties_dialog.cpp \ - module/groove.cpp \ gui/groove_settings_dialog.cpp \ gui/configuration_dialog.cpp \ command/pattern/expand_pattern_command.cpp \ - gui/command/pattern/expand_pattern_qt_command.cpp \ command/pattern/shrink_pattern_command.cpp \ - gui/command/pattern/shrink_pattern_qt_command.cpp \ instrument/abstract_instrument_property.cpp \ command/order/duplicate_order_command.cpp \ command/order/move_order_command.cpp \ command/order/clone_patterns_command.cpp \ command/order/clone_order_command.cpp \ - gui/command/order/duplicate_order_qt_command.cpp \ - gui/command/order/move_order_qt_command.cpp \ - gui/command/order/clone_patterns_qt_command.cpp \ - gui/command/order/clone_order_qt_command.cpp \ - gui/command/pattern/set_echo_buffer_access_qt_command.cpp \ command/pattern/set_echo_buffer_access_command.cpp \ gui/comment_edit_dialog.cpp \ io/binary_container.cpp \ - gui/command/pattern/interpolate_pattern_qt_command.cpp \ command/pattern/interpolate_pattern_command.cpp \ - gui/command/pattern/reverse_pattern_qt_command.cpp \ command/pattern/reverse_pattern_command.cpp \ - gui/command/pattern/replace_instrument_in_pattern_qt_command.cpp \ command/pattern/replace_instrument_in_pattern_command.cpp \ gui/vgm_export_settings_dialog.cpp \ gui/wave_export_settings_dialog.cpp \ configuration.cpp \ gui/configuration_handler.cpp \ gui/color_palette.cpp \ - gui/command/pattern/paste_overwrite_copied_data_to_pattern_qt_command.cpp \ command/pattern/paste_overwrite_copied_data_to_pattern_command.cpp \ format/wopn_file.c \ instrument/bank.cpp \ @@ -321,7 +253,7 @@ gui/wave_visual.cpp HEADERS += \ - calc_pitch.hpp \ + bamboo_tracker_defs.hpp \ chip/codec/ymb_codec.hpp \ chip/c86ctl/c86ctl.h \ chip/c86ctl/c86ctl_wrapper.hpp \ @@ -334,14 +266,16 @@ command/pattern/paste_insert_copied_data_to_pattern_command.hpp \ command/pattern/pattern_command_utils.hpp \ command/pattern/transpose_note_in_pattern_command.hpp \ + echo_buffer.hpp \ enum_hash.hpp \ gui/bookmark_manager_form.hpp \ gui/color_palette_handler.hpp \ gui/command/instrument/instrument_command_qt_utils.hpp \ + gui/command/instrument/instrument_commands_qt.hpp \ gui/command/instrument/swap_instruments_qt_command.hpp \ - gui/command/pattern/change_values_in_pattern_qt_command.hpp \ - gui/command/pattern/paste_insert_copied_data_to_pattern_qt_command.hpp \ - gui/command/pattern/transpose_note_in_pattern_qt_command.hpp \ + gui/command/order/order_commands_qt.hpp \ + gui/command/order/order_list_common_qt_command.hpp \ + gui/command/pattern/pattern_editor_common_qt_command.hpp \ gui/drop_detect_list_widget.hpp \ gui/effect_description.hpp \ gui/effect_list_dialog.hpp \ @@ -375,7 +309,9 @@ gui/swap_tracks_dialog.hpp \ gui/track_visibility_memory_handler.hpp \ gui/transpose_song_dialog.hpp \ - instrument/waveform_adpcm.hpp \ + instrument/instrument_property_defs.hpp \ + instrument/sample_adpcm.hpp \ + instrument/sequence_property.hpp \ io/btb_io.hpp \ io/bti_io.hpp \ io/btm_io.hpp \ @@ -387,8 +323,11 @@ io/io_file_type.hpp \ io/io_utils.hpp \ io/opni_io.hpp \ + io/p86_io.hpp \ io/ppc_io.hpp \ + io/pps_io.hpp \ io/pvi_io.hpp \ + io/pzi_io.hpp \ io/tfi_io.hpp \ io/vgi_io.hpp \ io/wav_container.hpp \ @@ -396,18 +335,17 @@ io/y12_io.hpp \ jamming.hpp \ module/effect.hpp \ + note.hpp \ playback.hpp \ song_length_calculator.hpp \ audio/audio_stream.hpp \ chip/chip_def.h \ - misc.hpp \ instrument/instruments_manager.hpp \ command/command_manager.hpp \ command/instrument/add_instrument_command.hpp \ command/instrument/remove_instrument_command.hpp \ command/commands.hpp \ gui/command/instrument/add_instrument_qt_command.hpp \ - gui/command/commands_qt.hpp \ gui/command/instrument/remove_instrument_qt_command.hpp \ gui/instrument_editor/instrument_editor_fm_form.hpp \ gui/instrument_editor/fm_operator_table.hpp \ @@ -432,33 +370,20 @@ gui/pattern_editor/pattern_editor_panel.hpp \ gui/pattern_editor/pattern_editor.hpp \ gui/instrument_editor/instrument_editor_ssg_form.hpp \ - gui/command/pattern/set_key_off_to_step_qt_command.hpp \ command/pattern/set_key_off_to_step_command.hpp \ gui/command/pattern/pattern_commands_qt.hpp \ command/pattern/set_key_on_to_step_command.hpp \ - gui/command/pattern/set_key_on_to_step_qt_command.hpp \ gui/pattern_editor/pattern_position.hpp \ - gui/command/pattern/set_instrument_to_step_qt_command.hpp \ command/pattern/set_instrument_to_step_command.hpp \ - gui/command/pattern/erase_instrument_in_step_qt_command.hpp \ command/pattern/erase_instrument_in_step_command.hpp \ - gui/command/pattern/set_volume_to_step_qt_command.hpp \ command/pattern/set_volume_to_step_command.hpp \ - gui/command/pattern/erase_volume_in_step_qt_command.hpp \ command/pattern/erase_volume_in_step_command.hpp \ command/pattern/set_effect_id_to_step_command.hpp \ - gui/command/pattern/set_effect_id_to_step_qt_command.hpp \ command/pattern/erase_effect_in_step_command.hpp \ - gui/command/pattern/erase_effect_in_step_qt_command.hpp \ command/pattern/set_effect_value_to_step_command.hpp \ - gui/command/pattern/set_effect_value_to_step_qt_command.hpp \ command/pattern/erase_effect_value_in_step_command.hpp \ - gui/command/pattern/erase_effect_value_in_step_qt_command.hpp \ command/pattern/insert_step_command.hpp \ command/pattern/delete_previous_step_command.hpp \ - gui/command/pattern/insert_step_qt_command.hpp \ - gui/command/pattern/delete_previous_step_qt_command.hpp \ - gui/command/pattern/erase_step_qt_command.hpp \ command/pattern/erase_step_command.hpp \ gui/command/instrument/deep_clone_instrument_qt_command.hpp \ command/instrument/deep_clone_instrument_command.hpp \ @@ -466,61 +391,41 @@ gui/command/instrument/clone_instrument_qt_command.hpp \ gui/order_list_editor/order_position.hpp \ command/order/set_pattern_to_order_command.hpp \ - gui/command/order/set_pattern_to_order_qt_command.hpp \ - gui/command/order/order_commands.hpp \ command/order/insert_order_below_command.hpp \ command/order/delete_order_command.hpp \ - gui/command/order/insert_order_below_qt_command.hpp \ - gui/command/order/delete_order_qt_command.hpp \ command/pattern/paste_copied_data_to_pattern_command.hpp \ - gui/command/pattern/paste_copied_data_to_pattern_qt_command.hpp \ command/pattern/erase_cells_in_pattern_command.hpp \ - gui/command/pattern/erase_cells_in_pattern_qt_command.hpp \ command/order/paste_copied_data_to_order_command.hpp \ - gui/command/order/paste_copied_data_to_order_qt_command.hpp \ gui/instrument_editor/instrument_form_manager.hpp \ instrument/lfo_fm.hpp \ gui/instrument_editor/visualized_instrument_macro_editor.hpp \ - instrument/command_sequence.hpp \ instrument/sequence_iterator_interface.hpp \ instrument/effect_iterator.hpp \ command/pattern/paste_mix_copied_data_to_pattern_command.hpp \ - gui/command/pattern/paste_mix_copied_data_to_pattern_qt_command.hpp \ gui/module_properties_dialog.hpp \ - module/groove.hpp \ gui/groove_settings_dialog.hpp \ gui/configuration_dialog.hpp \ command/pattern/expand_pattern_command.hpp \ - gui/command/pattern/expand_pattern_qt_command.hpp \ command/pattern/shrink_pattern_command.hpp \ - gui/command/pattern/shrink_pattern_qt_command.hpp \ command/abstract_command.hpp \ instrument/abstract_instrument_property.hpp \ command/order/duplicate_order_command.hpp \ command/order/move_order_command.hpp \ command/order/clone_patterns_command.hpp \ command/order/clone_order_command.hpp \ - gui/command/order/duplicate_order_qt_command.hpp \ - gui/command/order/move_order_qt_command.hpp \ - gui/command/order/clone_patterns_qt_command.hpp \ - gui/command/order/clone_order_qt_command.hpp \ - gui/command/pattern/set_echo_buffer_access_qt_command.hpp \ command/pattern/set_echo_buffer_access_command.hpp \ gui/comment_edit_dialog.hpp \ io/binary_container.hpp \ + utils.hpp \ version.hpp \ - gui/command/pattern/interpolate_pattern_qt_command.hpp \ command/pattern/interpolate_pattern_command.hpp \ - gui/command/pattern/reverse_pattern_qt_command.hpp \ command/pattern/reverse_pattern_command.hpp \ - gui/command/pattern/replace_instrument_in_pattern_qt_command.hpp \ command/pattern/replace_instrument_in_pattern_command.hpp \ gui/vgm_export_settings_dialog.hpp \ gui/wave_export_settings_dialog.hpp \ configuration.hpp \ gui/configuration_handler.hpp \ gui/color_palette.hpp \ - gui/command/pattern/paste_overwrite_copied_data_to_pattern_qt_command.hpp \ command/pattern/paste_overwrite_copied_data_to_pattern_command.hpp \ io/file_io_error.hpp \ format/wopn_file.h \ @@ -574,8 +479,32 @@ $$PWD/instrument \ $$PWD/module -include("audio/RtAudio/RtAudio.pri") -include("midi/RtMidi/RtMidi.pri") +# In-app resource bundle. Needs to be handled here because it generates an object file to link against +include("resources/resources.pri") -include("../data/data.pri") +!system_rtaudio|!system_rtmidi { + CONFIG += link_prl +} +system_* { + CONFIG += link_pkgconfig +} +system_rtaudio { + PKGCONFIG += rtaudio +} +else { + INCLUDEPATH += $$PWD/../submodules/RtAudio/src + LIBS += -L$$OUT_PWD/../submodules/RtAudio + CONFIG(debug, debug|release):LIBS += -lrtaudiod + else:CONFIG(release, debug|release):LIBS += -lrtaudio +} + +system_rtmidi { + PKGCONFIG += rtmidi +} +else { + INCLUDEPATH += $$PWD/../submodules/RtMidi/src + LIBS += -L$$OUT_PWD/../submodules/RtMidi + CONFIG(debug, debug|release):LIBS += -lrtmidid + else:CONFIG(release, debug|release):LIBS += -lrtmidi +} diff -Nru bambootracker-0.4.5+git20121209/BambooTracker/calc_pitch.cpp bambootracker-0.4.6/BambooTracker/calc_pitch.cpp --- bambootracker-0.4.5+git20121209/BambooTracker/calc_pitch.cpp 2020-12-08 14:05:28.000000000 +0000 +++ bambootracker-0.4.6/BambooTracker/calc_pitch.cpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,1107 +0,0 @@ -/* - * Copyright (C) 2018-2020 Rerrah - * - * Permission is hereby granted, free of charge, to any person - * obtaining a copy of this software and associated documentation - * files (the "Software"), to deal in the Software without - * restriction, including without limitation the rights to use, - * copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following - * conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES - * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT - * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, - * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR - * OTHER DEALINGS IN THE SOFTWARE. - */ - -#include "calc_pitch.hpp" - -namespace -{ -const uint16_t centTableFM[3072] = { - 0x026a, 0x026b, 0x026c, 0x026e, 0x026f, 0x0270, 0x0271, 0x0272, 0x0273, 0x0274, 0x0276, 0x0277, - 0x0278, 0x0279, 0x027a, 0x027b, 0x027c, 0x027e, 0x027f, 0x0280, 0x0281, 0x0282, 0x0283, 0x0284, - 0x0286, 0x0287, 0x0288, 0x0289, 0x028a, 0x028b, 0x028d, 0x028e, 0x028f, 0x0290, 0x0291, 0x0293, - 0x0294, 0x0295, 0x0296, 0x0297, 0x0299, 0x029a, 0x029b, 0x029c, 0x029d, 0x029f, 0x02a0, 0x02a1, - 0x02a2, 0x02a3, 0x02a5, 0x02a6, 0x02a7, 0x02a8, 0x02aa, 0x02ab, 0x02ac, 0x02ad, 0x02ae, 0x02b0, - 0x02b1, 0x02b2, 0x02b3, 0x02b5, 0x02b6, 0x02b7, 0x02b8, 0x02ba, 0x02bb, 0x02bc, 0x02be, 0x02bf, - 0x02c0, 0x02c1, 0x02c3, 0x02c4, 0x02c5, 0x02c6, 0x02c8, 0x02c9, 0x02ca, 0x02cc, 0x02cd, 0x02ce, - 0x02cf, 0x02d1, 0x02d2, 0x02d3, 0x02d5, 0x02d6, 0x02d7, 0x02d9, 0x02da, 0x02db, 0x02dd, 0x02de, - 0x02df, 0x02e1, 0x02e2, 0x02e3, 0x02e5, 0x02e6, 0x02e7, 0x02e9, 0x02ea, 0x02eb, 0x02ed, 0x02ee, - 0x02ef, 0x02f1, 0x02f2, 0x02f3, 0x02f5, 0x02f6, 0x02f7, 0x02f9, 0x02fa, 0x02fc, 0x02fd, 0x02fe, - 0x0300, 0x0301, 0x0303, 0x0304, 0x0305, 0x0307, 0x0308, 0x030a, 0x030b, 0x030c, 0x030e, 0x030f, - 0x0311, 0x0312, 0x0313, 0x0315, 0x0316, 0x0318, 0x0319, 0x031b, 0x031c, 0x031d, 0x031f, 0x0320, - 0x0322, 0x0323, 0x0325, 0x0326, 0x0328, 0x0329, 0x032a, 0x032c, 0x032d, 0x032f, 0x0330, 0x0332, - 0x0333, 0x0335, 0x0336, 0x0338, 0x0339, 0x033b, 0x033c, 0x033e, 0x033f, 0x0341, 0x0342, 0x0344, - 0x0345, 0x0347, 0x0348, 0x034a, 0x034b, 0x034d, 0x034e, 0x0350, 0x0351, 0x0353, 0x0355, 0x0356, - 0x0358, 0x0359, 0x035b, 0x035c, 0x035e, 0x035f, 0x0361, 0x0362, 0x0364, 0x0366, 0x0367, 0x0369, - 0x036a, 0x036c, 0x036d, 0x036f, 0x0371, 0x0372, 0x0374, 0x0375, 0x0377, 0x0379, 0x037a, 0x037c, - 0x037d, 0x037f, 0x0381, 0x0382, 0x0384, 0x0386, 0x0387, 0x0389, 0x038a, 0x038c, 0x038e, 0x038f, - 0x0391, 0x0393, 0x0394, 0x0396, 0x0398, 0x0399, 0x039b, 0x039d, 0x039e, 0x03a0, 0x03a2, 0x03a3, - 0x03a5, 0x03a7, 0x03a8, 0x03aa, 0x03ac, 0x03ad, 0x03af, 0x03b1, 0x03b3, 0x03b4, 0x03b6, 0x03b8, - 0x03b9, 0x03bb, 0x03bd, 0x03bf, 0x03c0, 0x03c2, 0x03c4, 0x03c6, 0x03c7, 0x03c9, 0x03cb, 0x03cd, - 0x03ce, 0x03d0, 0x03d2, 0x03d4, 0x03d5, 0x03d7, 0x03d9, 0x03db, 0x03dd, 0x03de, 0x03e0, 0x03e2, - 0x03e4, 0x03e5, 0x03e7, 0x03e9, 0x03eb, 0x03ed, 0x03ef, 0x03f0, 0x03f2, 0x03f4, 0x03f6, 0x03f8, - 0x03f9, 0x03fb, 0x03fd, 0x03ff, 0x0401, 0x0403, 0x0405, 0x0406, 0x0408, 0x040a, 0x040c, 0x040e, - 0x0410, 0x0412, 0x0414, 0x0415, 0x0417, 0x0419, 0x041b, 0x041d, 0x041f, 0x0421, 0x0423, 0x0425, - 0x0427, 0x0428, 0x042a, 0x042c, 0x042e, 0x0430, 0x0432, 0x0434, 0x0436, 0x0438, 0x043a, 0x043c, - 0x043e, 0x0440, 0x0442, 0x0444, 0x0446, 0x0448, 0x044a, 0x044c, 0x044e, 0x0450, 0x0452, 0x0454, - 0x0456, 0x0458, 0x045a, 0x045c, 0x045e, 0x0460, 0x0462, 0x0464, 0x0466, 0x0468, 0x046a, 0x046c, - 0x046e, 0x0470, 0x0472, 0x0474, 0x0476, 0x0478, 0x047a, 0x047c, 0x047e, 0x0480, 0x0483, 0x0485, - 0x0487, 0x0489, 0x048b, 0x048d, 0x048f, 0x0491, 0x0493, 0x0495, 0x0498, 0x049a, 0x049c, 0x049e, - 0x04a0, 0x04a2, 0x04a4, 0x04a6, 0x04a9, 0x04ab, 0x04ad, 0x04af, 0x04b1, 0x04b3, 0x04b6, 0x04b8, - 0x04ba, 0x04bc, 0x04be, 0x04c1, 0x04c3, 0x04c5, 0x04c7, 0x04c9, 0x04cc, 0x04ce, 0x04d0, 0x04d2, - 0x04d4, 0x04d7, 0x04d9, 0x04db, 0x04dd, 0x04e0, 0x04e2, 0x04e4, 0x04e6, 0x04e9, 0x04eb, 0x04ed, - 0x04f0, 0x04f2, 0x04f4, 0x04f6, 0x04f9, 0x04fb, 0x04fd, 0x0500, 0x0502, 0x0504, 0x0507, 0x0509, - 0x050b, 0x050e, 0x0510, 0x0512, 0x0515, 0x0517, 0x0519, 0x051c, 0x051e, 0x0520, 0x0523, 0x0525, - 0x0528, 0x052a, 0x052c, 0x052f, 0x0531, 0x0533, 0x0536, 0x0538, 0x053b, 0x053d, 0x0540, 0x0542, - 0x0544, 0x0547, 0x0549, 0x054c, 0x054e, 0x0551, 0x0553, 0x0556, 0x0558, 0x055a, 0x055d, 0x055f, - 0x0562, 0x0564, 0x0567, 0x0569, 0x056c, 0x056e, 0x0571, 0x0573, 0x0576, 0x0578, 0x057b, 0x057e, - 0x0580, 0x0583, 0x0585, 0x0588, 0x058a, 0x058d, 0x058f, 0x0592, 0x0595, 0x0597, 0x059a, 0x059c, - 0x059f, 0x05a2, 0x05a4, 0x05a7, 0x05a9, 0x05ac, 0x05af, 0x05b1, 0x05b4, 0x05b6, 0x05b9, 0x05bc, - 0x05be, 0x05c1, 0x05c4, 0x05c6, 0x05c9, 0x05cc, 0x05ce, 0x05d1, 0x05d4, 0x05d7, 0x05d9, 0x05dc, - 0x05df, 0x05e1, 0x05e4, 0x05e7, 0x05ea, 0x05ec, 0x05ef, 0x05f2, 0x05f4, 0x05f7, 0x05fa, 0x05fd, - 0x0600, 0x0602, 0x0605, 0x0608, 0x060b, 0x060d, 0x0610, 0x0613, 0x0616, 0x0619, 0x061c, 0x061e, - 0x0621, 0x0624, 0x0627, 0x062a, 0x062d, 0x062f, 0x0632, 0x0635, 0x0638, 0x063b, 0x063e, 0x0641, - 0x0644, 0x0646, 0x0649, 0x064c, 0x064f, 0x0652, 0x0655, 0x0658, 0x065b, 0x065e, 0x0661, 0x0664, - 0x0667, 0x066a, 0x066d, 0x0670, 0x0673, 0x0675, 0x0678, 0x067b, 0x067e, 0x0681, 0x0684, 0x0687, - 0x068b, 0x068e, 0x0691, 0x0694, 0x0697, 0x069a, 0x069d, 0x06a0, 0x06a3, 0x06a6, 0x06a9, 0x06ac, - 0x06af, 0x06b2, 0x06b5, 0x06b8, 0x06bc, 0x06bf, 0x06c2, 0x06c5, 0x06c8, 0x06cb, 0x06ce, 0x06d1, - 0x06d5, 0x06d8, 0x06db, 0x06de, 0x06e1, 0x06e5, 0x06e8, 0x06eb, 0x06ee, 0x06f1, 0x06f5, 0x06f8, - 0x06fb, 0x06fe, 0x0701, 0x0705, 0x0708, 0x070b, 0x070e, 0x0712, 0x0715, 0x0718, 0x071b, 0x071f, - 0x0722, 0x0725, 0x0729, 0x072c, 0x072f, 0x0733, 0x0736, 0x0739, 0x073d, 0x0740, 0x0743, 0x0747, - 0x074a, 0x074d, 0x0751, 0x0754, 0x0758, 0x075b, 0x075e, 0x0762, 0x0765, 0x0769, 0x076c, 0x076f, - 0x0773, 0x0776, 0x077a, 0x077d, 0x0781, 0x0784, 0x0788, 0x078b, 0x078f, 0x0792, 0x0796, 0x0799, - 0x079d, 0x07a0, 0x07a4, 0x07a7, 0x07ab, 0x07ae, 0x07b2, 0x07b5, 0x07b9, 0x07bd, 0x07c0, 0x07c4, - 0x07c7, 0x07cb, 0x07cf, 0x07d2, 0x07d6, 0x07d9, 0x07dd, 0x07e1, 0x07e4, 0x07e8, 0x07ec, 0x07ef, - 0x07f3, 0x07f7, 0x07fa, 0x07fe, 0x0c01, 0x0c03, 0x0c05, 0x0c06, 0x0c08, 0x0c0a, 0x0c0c, 0x0c0e, - 0x0c10, 0x0c12, 0x0c14, 0x0c15, 0x0c17, 0x0c19, 0x0c1b, 0x0c1d, 0x0c1f, 0x0c21, 0x0c23, 0x0c25, - 0x0c27, 0x0c28, 0x0c2a, 0x0c2c, 0x0c2e, 0x0c30, 0x0c32, 0x0c34, 0x0c36, 0x0c38, 0x0c3a, 0x0c3c, - 0x0c3e, 0x0c40, 0x0c42, 0x0c44, 0x0c46, 0x0c48, 0x0c4a, 0x0c4c, 0x0c4e, 0x0c50, 0x0c52, 0x0c54, - 0x0c56, 0x0c58, 0x0c5a, 0x0c5c, 0x0c5e, 0x0c60, 0x0c62, 0x0c64, 0x0c66, 0x0c68, 0x0c6a, 0x0c6c, - 0x0c6e, 0x0c70, 0x0c72, 0x0c74, 0x0c76, 0x0c78, 0x0c7a, 0x0c7c, 0x0c7e, 0x0c80, 0x0c83, 0x0c85, - 0x0c87, 0x0c89, 0x0c8b, 0x0c8d, 0x0c8f, 0x0c91, 0x0c93, 0x0c95, 0x0c98, 0x0c9a, 0x0c9c, 0x0c9e, - 0x0ca0, 0x0ca2, 0x0ca4, 0x0ca6, 0x0ca9, 0x0cab, 0x0cad, 0x0caf, 0x0cb1, 0x0cb3, 0x0cb6, 0x0cb8, - 0x0cba, 0x0cbc, 0x0cbe, 0x0cc1, 0x0cc3, 0x0cc5, 0x0cc7, 0x0cc9, 0x0ccc, 0x0cce, 0x0cd0, 0x0cd2, - 0x0cd4, 0x0cd7, 0x0cd9, 0x0cdb, 0x0cdd, 0x0ce0, 0x0ce2, 0x0ce4, 0x0ce6, 0x0ce9, 0x0ceb, 0x0ced, - 0x0cf0, 0x0cf2, 0x0cf4, 0x0cf6, 0x0cf9, 0x0cfb, 0x0cfd, 0x0d00, 0x0d02, 0x0d04, 0x0d07, 0x0d09, - 0x0d0b, 0x0d0e, 0x0d10, 0x0d12, 0x0d15, 0x0d17, 0x0d19, 0x0d1c, 0x0d1e, 0x0d20, 0x0d23, 0x0d25, - 0x0d28, 0x0d2a, 0x0d2c, 0x0d2f, 0x0d31, 0x0d33, 0x0d36, 0x0d38, 0x0d3b, 0x0d3d, 0x0d40, 0x0d42, - 0x0d44, 0x0d47, 0x0d49, 0x0d4c, 0x0d4e, 0x0d51, 0x0d53, 0x0d56, 0x0d58, 0x0d5a, 0x0d5d, 0x0d5f, - 0x0d62, 0x0d64, 0x0d67, 0x0d69, 0x0d6c, 0x0d6e, 0x0d71, 0x0d73, 0x0d76, 0x0d78, 0x0d7b, 0x0d7e, - 0x0d80, 0x0d83, 0x0d85, 0x0d88, 0x0d8a, 0x0d8d, 0x0d8f, 0x0d92, 0x0d95, 0x0d97, 0x0d9a, 0x0d9c, - 0x0d9f, 0x0da2, 0x0da4, 0x0da7, 0x0da9, 0x0dac, 0x0daf, 0x0db1, 0x0db4, 0x0db6, 0x0db9, 0x0dbc, - 0x0dbe, 0x0dc1, 0x0dc4, 0x0dc6, 0x0dc9, 0x0dcc, 0x0dce, 0x0dd1, 0x0dd4, 0x0dd7, 0x0dd9, 0x0ddc, - 0x0ddf, 0x0de1, 0x0de4, 0x0de7, 0x0dea, 0x0dec, 0x0def, 0x0df2, 0x0df4, 0x0df7, 0x0dfa, 0x0dfd, - 0x0e00, 0x0e02, 0x0e05, 0x0e08, 0x0e0b, 0x0e0d, 0x0e10, 0x0e13, 0x0e16, 0x0e19, 0x0e1c, 0x0e1e, - 0x0e21, 0x0e24, 0x0e27, 0x0e2a, 0x0e2d, 0x0e2f, 0x0e32, 0x0e35, 0x0e38, 0x0e3b, 0x0e3e, 0x0e41, - 0x0e44, 0x0e46, 0x0e49, 0x0e4c, 0x0e4f, 0x0e52, 0x0e55, 0x0e58, 0x0e5b, 0x0e5e, 0x0e61, 0x0e64, - 0x0e67, 0x0e6a, 0x0e6d, 0x0e70, 0x0e73, 0x0e75, 0x0e78, 0x0e7b, 0x0e7e, 0x0e81, 0x0e84, 0x0e87, - 0x0e8b, 0x0e8e, 0x0e91, 0x0e94, 0x0e97, 0x0e9a, 0x0e9d, 0x0ea0, 0x0ea3, 0x0ea6, 0x0ea9, 0x0eac, - 0x0eaf, 0x0eb2, 0x0eb5, 0x0eb8, 0x0ebc, 0x0ebf, 0x0ec2, 0x0ec5, 0x0ec8, 0x0ecb, 0x0ece, 0x0ed1, - 0x0ed5, 0x0ed8, 0x0edb, 0x0ede, 0x0ee1, 0x0ee5, 0x0ee8, 0x0eeb, 0x0eee, 0x0ef1, 0x0ef5, 0x0ef8, - 0x0efb, 0x0efe, 0x0f01, 0x0f05, 0x0f08, 0x0f0b, 0x0f0e, 0x0f12, 0x0f15, 0x0f18, 0x0f1b, 0x0f1f, - 0x0f22, 0x0f25, 0x0f29, 0x0f2c, 0x0f2f, 0x0f33, 0x0f36, 0x0f39, 0x0f3d, 0x0f40, 0x0f43, 0x0f47, - 0x0f4a, 0x0f4d, 0x0f51, 0x0f54, 0x0f58, 0x0f5b, 0x0f5e, 0x0f62, 0x0f65, 0x0f69, 0x0f6c, 0x0f6f, - 0x0f73, 0x0f76, 0x0f7a, 0x0f7d, 0x0f81, 0x0f84, 0x0f88, 0x0f8b, 0x0f8f, 0x0f92, 0x0f96, 0x0f99, - 0x0f9d, 0x0fa0, 0x0fa4, 0x0fa7, 0x0fab, 0x0fae, 0x0fb2, 0x0fb5, 0x0fb9, 0x0fbd, 0x0fc0, 0x0fc4, - 0x0fc7, 0x0fcb, 0x0fcf, 0x0fd2, 0x0fd6, 0x0fd9, 0x0fdd, 0x0fe1, 0x0fe4, 0x0fe8, 0x0fec, 0x0fef, - 0x0ff3, 0x0ff7, 0x0ffa, 0x0ffe, 0x1401, 0x1403, 0x1405, 0x1406, 0x1408, 0x140a, 0x140c, 0x140e, - 0x1410, 0x1412, 0x1414, 0x1415, 0x1417, 0x1419, 0x141b, 0x141d, 0x141f, 0x1421, 0x1423, 0x1425, - 0x1427, 0x1428, 0x142a, 0x142c, 0x142e, 0x1430, 0x1432, 0x1434, 0x1436, 0x1438, 0x143a, 0x143c, - 0x143e, 0x1440, 0x1442, 0x1444, 0x1446, 0x1448, 0x144a, 0x144c, 0x144e, 0x1450, 0x1452, 0x1454, - 0x1456, 0x1458, 0x145a, 0x145c, 0x145e, 0x1460, 0x1462, 0x1464, 0x1466, 0x1468, 0x146a, 0x146c, - 0x146e, 0x1470, 0x1472, 0x1474, 0x1476, 0x1478, 0x147a, 0x147c, 0x147e, 0x1480, 0x1483, 0x1485, - 0x1487, 0x1489, 0x148b, 0x148d, 0x148f, 0x1491, 0x1493, 0x1495, 0x1498, 0x149a, 0x149c, 0x149e, - 0x14a0, 0x14a2, 0x14a4, 0x14a6, 0x14a9, 0x14ab, 0x14ad, 0x14af, 0x14b1, 0x14b3, 0x14b6, 0x14b8, - 0x14ba, 0x14bc, 0x14be, 0x14c1, 0x14c3, 0x14c5, 0x14c7, 0x14c9, 0x14cc, 0x14ce, 0x14d0, 0x14d2, - 0x14d4, 0x14d7, 0x14d9, 0x14db, 0x14dd, 0x14e0, 0x14e2, 0x14e4, 0x14e6, 0x14e9, 0x14eb, 0x14ed, - 0x14f0, 0x14f2, 0x14f4, 0x14f6, 0x14f9, 0x14fb, 0x14fd, 0x1500, 0x1502, 0x1504, 0x1507, 0x1509, - 0x150b, 0x150e, 0x1510, 0x1512, 0x1515, 0x1517, 0x1519, 0x151c, 0x151e, 0x1520, 0x1523, 0x1525, - 0x1528, 0x152a, 0x152c, 0x152f, 0x1531, 0x1533, 0x1536, 0x1538, 0x153b, 0x153d, 0x1540, 0x1542, - 0x1544, 0x1547, 0x1549, 0x154c, 0x154e, 0x1551, 0x1553, 0x1556, 0x1558, 0x155a, 0x155d, 0x155f, - 0x1562, 0x1564, 0x1567, 0x1569, 0x156c, 0x156e, 0x1571, 0x1573, 0x1576, 0x1578, 0x157b, 0x157e, - 0x1580, 0x1583, 0x1585, 0x1588, 0x158a, 0x158d, 0x158f, 0x1592, 0x1595, 0x1597, 0x159a, 0x159c, - 0x159f, 0x15a2, 0x15a4, 0x15a7, 0x15a9, 0x15ac, 0x15af, 0x15b1, 0x15b4, 0x15b6, 0x15b9, 0x15bc, - 0x15be, 0x15c1, 0x15c4, 0x15c6, 0x15c9, 0x15cc, 0x15ce, 0x15d1, 0x15d4, 0x15d7, 0x15d9, 0x15dc, - 0x15df, 0x15e1, 0x15e4, 0x15e7, 0x15ea, 0x15ec, 0x15ef, 0x15f2, 0x15f4, 0x15f7, 0x15fa, 0x15fd, - 0x1600, 0x1602, 0x1605, 0x1608, 0x160b, 0x160d, 0x1610, 0x1613, 0x1616, 0x1619, 0x161c, 0x161e, - 0x1621, 0x1624, 0x1627, 0x162a, 0x162d, 0x162f, 0x1632, 0x1635, 0x1638, 0x163b, 0x163e, 0x1641, - 0x1644, 0x1646, 0x1649, 0x164c, 0x164f, 0x1652, 0x1655, 0x1658, 0x165b, 0x165e, 0x1661, 0x1664, - 0x1667, 0x166a, 0x166d, 0x1670, 0x1673, 0x1675, 0x1678, 0x167b, 0x167e, 0x1681, 0x1684, 0x1687, - 0x168b, 0x168e, 0x1691, 0x1694, 0x1697, 0x169a, 0x169d, 0x16a0, 0x16a3, 0x16a6, 0x16a9, 0x16ac, - 0x16af, 0x16b2, 0x16b5, 0x16b8, 0x16bc, 0x16bf, 0x16c2, 0x16c5, 0x16c8, 0x16cb, 0x16ce, 0x16d1, - 0x16d5, 0x16d8, 0x16db, 0x16de, 0x16e1, 0x16e5, 0x16e8, 0x16eb, 0x16ee, 0x16f1, 0x16f5, 0x16f8, - 0x16fb, 0x16fe, 0x1701, 0x1705, 0x1708, 0x170b, 0x170e, 0x1712, 0x1715, 0x1718, 0x171b, 0x171f, - 0x1722, 0x1725, 0x1729, 0x172c, 0x172f, 0x1733, 0x1736, 0x1739, 0x173d, 0x1740, 0x1743, 0x1747, - 0x174a, 0x174d, 0x1751, 0x1754, 0x1758, 0x175b, 0x175e, 0x1762, 0x1765, 0x1769, 0x176c, 0x176f, - 0x1773, 0x1776, 0x177a, 0x177d, 0x1781, 0x1784, 0x1788, 0x178b, 0x178f, 0x1792, 0x1796, 0x1799, - 0x179d, 0x17a0, 0x17a4, 0x17a7, 0x17ab, 0x17ae, 0x17b2, 0x17b5, 0x17b9, 0x17bd, 0x17c0, 0x17c4, - 0x17c7, 0x17cb, 0x17cf, 0x17d2, 0x17d6, 0x17d9, 0x17dd, 0x17e1, 0x17e4, 0x17e8, 0x17ec, 0x17ef, - 0x17f3, 0x17f7, 0x17fa, 0x17fe, 0x1c01, 0x1c03, 0x1c05, 0x1c06, 0x1c08, 0x1c0a, 0x1c0c, 0x1c0e, - 0x1c10, 0x1c12, 0x1c14, 0x1c15, 0x1c17, 0x1c19, 0x1c1b, 0x1c1d, 0x1c1f, 0x1c21, 0x1c23, 0x1c25, - 0x1c27, 0x1c28, 0x1c2a, 0x1c2c, 0x1c2e, 0x1c30, 0x1c32, 0x1c34, 0x1c36, 0x1c38, 0x1c3a, 0x1c3c, - 0x1c3e, 0x1c40, 0x1c42, 0x1c44, 0x1c46, 0x1c48, 0x1c4a, 0x1c4c, 0x1c4e, 0x1c50, 0x1c52, 0x1c54, - 0x1c56, 0x1c58, 0x1c5a, 0x1c5c, 0x1c5e, 0x1c60, 0x1c62, 0x1c64, 0x1c66, 0x1c68, 0x1c6a, 0x1c6c, - 0x1c6e, 0x1c70, 0x1c72, 0x1c74, 0x1c76, 0x1c78, 0x1c7a, 0x1c7c, 0x1c7e, 0x1c80, 0x1c83, 0x1c85, - 0x1c87, 0x1c89, 0x1c8b, 0x1c8d, 0x1c8f, 0x1c91, 0x1c93, 0x1c95, 0x1c98, 0x1c9a, 0x1c9c, 0x1c9e, - 0x1ca0, 0x1ca2, 0x1ca4, 0x1ca6, 0x1ca9, 0x1cab, 0x1cad, 0x1caf, 0x1cb1, 0x1cb3, 0x1cb6, 0x1cb8, - 0x1cba, 0x1cbc, 0x1cbe, 0x1cc1, 0x1cc3, 0x1cc5, 0x1cc7, 0x1cc9, 0x1ccc, 0x1cce, 0x1cd0, 0x1cd2, - 0x1cd4, 0x1cd7, 0x1cd9, 0x1cdb, 0x1cdd, 0x1ce0, 0x1ce2, 0x1ce4, 0x1ce6, 0x1ce9, 0x1ceb, 0x1ced, - 0x1cf0, 0x1cf2, 0x1cf4, 0x1cf6, 0x1cf9, 0x1cfb, 0x1cfd, 0x1d00, 0x1d02, 0x1d04, 0x1d07, 0x1d09, - 0x1d0b, 0x1d0e, 0x1d10, 0x1d12, 0x1d15, 0x1d17, 0x1d19, 0x1d1c, 0x1d1e, 0x1d20, 0x1d23, 0x1d25, - 0x1d28, 0x1d2a, 0x1d2c, 0x1d2f, 0x1d31, 0x1d33, 0x1d36, 0x1d38, 0x1d3b, 0x1d3d, 0x1d40, 0x1d42, - 0x1d44, 0x1d47, 0x1d49, 0x1d4c, 0x1d4e, 0x1d51, 0x1d53, 0x1d56, 0x1d58, 0x1d5a, 0x1d5d, 0x1d5f, - 0x1d62, 0x1d64, 0x1d67, 0x1d69, 0x1d6c, 0x1d6e, 0x1d71, 0x1d73, 0x1d76, 0x1d78, 0x1d7b, 0x1d7e, - 0x1d80, 0x1d83, 0x1d85, 0x1d88, 0x1d8a, 0x1d8d, 0x1d8f, 0x1d92, 0x1d95, 0x1d97, 0x1d9a, 0x1d9c, - 0x1d9f, 0x1da2, 0x1da4, 0x1da7, 0x1da9, 0x1dac, 0x1daf, 0x1db1, 0x1db4, 0x1db6, 0x1db9, 0x1dbc, - 0x1dbe, 0x1dc1, 0x1dc4, 0x1dc6, 0x1dc9, 0x1dcc, 0x1dce, 0x1dd1, 0x1dd4, 0x1dd7, 0x1dd9, 0x1ddc, - 0x1ddf, 0x1de1, 0x1de4, 0x1de7, 0x1dea, 0x1dec, 0x1def, 0x1df2, 0x1df4, 0x1df7, 0x1dfa, 0x1dfd, - 0x1e00, 0x1e02, 0x1e05, 0x1e08, 0x1e0b, 0x1e0d, 0x1e10, 0x1e13, 0x1e16, 0x1e19, 0x1e1c, 0x1e1e, - 0x1e21, 0x1e24, 0x1e27, 0x1e2a, 0x1e2d, 0x1e2f, 0x1e32, 0x1e35, 0x1e38, 0x1e3b, 0x1e3e, 0x1e41, - 0x1e44, 0x1e46, 0x1e49, 0x1e4c, 0x1e4f, 0x1e52, 0x1e55, 0x1e58, 0x1e5b, 0x1e5e, 0x1e61, 0x1e64, - 0x1e67, 0x1e6a, 0x1e6d, 0x1e70, 0x1e73, 0x1e75, 0x1e78, 0x1e7b, 0x1e7e, 0x1e81, 0x1e84, 0x1e87, - 0x1e8b, 0x1e8e, 0x1e91, 0x1e94, 0x1e97, 0x1e9a, 0x1e9d, 0x1ea0, 0x1ea3, 0x1ea6, 0x1ea9, 0x1eac, - 0x1eaf, 0x1eb2, 0x1eb5, 0x1eb8, 0x1ebc, 0x1ebf, 0x1ec2, 0x1ec5, 0x1ec8, 0x1ecb, 0x1ece, 0x1ed1, - 0x1ed5, 0x1ed8, 0x1edb, 0x1ede, 0x1ee1, 0x1ee5, 0x1ee8, 0x1eeb, 0x1eee, 0x1ef1, 0x1ef5, 0x1ef8, - 0x1efb, 0x1efe, 0x1f01, 0x1f05, 0x1f08, 0x1f0b, 0x1f0e, 0x1f12, 0x1f15, 0x1f18, 0x1f1b, 0x1f1f, - 0x1f22, 0x1f25, 0x1f29, 0x1f2c, 0x1f2f, 0x1f33, 0x1f36, 0x1f39, 0x1f3d, 0x1f40, 0x1f43, 0x1f47, - 0x1f4a, 0x1f4d, 0x1f51, 0x1f54, 0x1f58, 0x1f5b, 0x1f5e, 0x1f62, 0x1f65, 0x1f69, 0x1f6c, 0x1f6f, - 0x1f73, 0x1f76, 0x1f7a, 0x1f7d, 0x1f81, 0x1f84, 0x1f88, 0x1f8b, 0x1f8f, 0x1f92, 0x1f96, 0x1f99, - 0x1f9d, 0x1fa0, 0x1fa4, 0x1fa7, 0x1fab, 0x1fae, 0x1fb2, 0x1fb5, 0x1fb9, 0x1fbd, 0x1fc0, 0x1fc4, - 0x1fc7, 0x1fcb, 0x1fcf, 0x1fd2, 0x1fd6, 0x1fd9, 0x1fdd, 0x1fe1, 0x1fe4, 0x1fe8, 0x1fec, 0x1fef, - 0x1ff3, 0x1ff7, 0x1ffa, 0x1ffe, 0x2401, 0x2403, 0x2405, 0x2406, 0x2408, 0x240a, 0x240c, 0x240e, - 0x2410, 0x2412, 0x2414, 0x2415, 0x2417, 0x2419, 0x241b, 0x241d, 0x241f, 0x2421, 0x2423, 0x2425, - 0x2427, 0x2428, 0x242a, 0x242c, 0x242e, 0x2430, 0x2432, 0x2434, 0x2436, 0x2438, 0x243a, 0x243c, - 0x243e, 0x2440, 0x2442, 0x2444, 0x2446, 0x2448, 0x244a, 0x244c, 0x244e, 0x2450, 0x2452, 0x2454, - 0x2456, 0x2458, 0x245a, 0x245c, 0x245e, 0x2460, 0x2462, 0x2464, 0x2466, 0x2468, 0x246a, 0x246c, - 0x246e, 0x2470, 0x2472, 0x2474, 0x2476, 0x2478, 0x247a, 0x247c, 0x247e, 0x2480, 0x2483, 0x2485, - 0x2487, 0x2489, 0x248b, 0x248d, 0x248f, 0x2491, 0x2493, 0x2495, 0x2498, 0x249a, 0x249c, 0x249e, - 0x24a0, 0x24a2, 0x24a4, 0x24a6, 0x24a9, 0x24ab, 0x24ad, 0x24af, 0x24b1, 0x24b3, 0x24b6, 0x24b8, - 0x24ba, 0x24bc, 0x24be, 0x24c1, 0x24c3, 0x24c5, 0x24c7, 0x24c9, 0x24cc, 0x24ce, 0x24d0, 0x24d2, - 0x24d4, 0x24d7, 0x24d9, 0x24db, 0x24dd, 0x24e0, 0x24e2, 0x24e4, 0x24e6, 0x24e9, 0x24eb, 0x24ed, - 0x24f0, 0x24f2, 0x24f4, 0x24f6, 0x24f9, 0x24fb, 0x24fd, 0x2500, 0x2502, 0x2504, 0x2507, 0x2509, - 0x250b, 0x250e, 0x2510, 0x2512, 0x2515, 0x2517, 0x2519, 0x251c, 0x251e, 0x2520, 0x2523, 0x2525, - 0x2528, 0x252a, 0x252c, 0x252f, 0x2531, 0x2533, 0x2536, 0x2538, 0x253b, 0x253d, 0x2540, 0x2542, - 0x2544, 0x2547, 0x2549, 0x254c, 0x254e, 0x2551, 0x2553, 0x2556, 0x2558, 0x255a, 0x255d, 0x255f, - 0x2562, 0x2564, 0x2567, 0x2569, 0x256c, 0x256e, 0x2571, 0x2573, 0x2576, 0x2578, 0x257b, 0x257e, - 0x2580, 0x2583, 0x2585, 0x2588, 0x258a, 0x258d, 0x258f, 0x2592, 0x2595, 0x2597, 0x259a, 0x259c, - 0x259f, 0x25a2, 0x25a4, 0x25a7, 0x25a9, 0x25ac, 0x25af, 0x25b1, 0x25b4, 0x25b6, 0x25b9, 0x25bc, - 0x25be, 0x25c1, 0x25c4, 0x25c6, 0x25c9, 0x25cc, 0x25ce, 0x25d1, 0x25d4, 0x25d7, 0x25d9, 0x25dc, - 0x25df, 0x25e1, 0x25e4, 0x25e7, 0x25ea, 0x25ec, 0x25ef, 0x25f2, 0x25f4, 0x25f7, 0x25fa, 0x25fd, - 0x2600, 0x2602, 0x2605, 0x2608, 0x260b, 0x260d, 0x2610, 0x2613, 0x2616, 0x2619, 0x261c, 0x261e, - 0x2621, 0x2624, 0x2627, 0x262a, 0x262d, 0x262f, 0x2632, 0x2635, 0x2638, 0x263b, 0x263e, 0x2641, - 0x2644, 0x2646, 0x2649, 0x264c, 0x264f, 0x2652, 0x2655, 0x2658, 0x265b, 0x265e, 0x2661, 0x2664, - 0x2667, 0x266a, 0x266d, 0x2670, 0x2673, 0x2675, 0x2678, 0x267b, 0x267e, 0x2681, 0x2684, 0x2687, - 0x268b, 0x268e, 0x2691, 0x2694, 0x2697, 0x269a, 0x269d, 0x26a0, 0x26a3, 0x26a6, 0x26a9, 0x26ac, - 0x26af, 0x26b2, 0x26b5, 0x26b8, 0x26bc, 0x26bf, 0x26c2, 0x26c5, 0x26c8, 0x26cb, 0x26ce, 0x26d1, - 0x26d5, 0x26d8, 0x26db, 0x26de, 0x26e1, 0x26e5, 0x26e8, 0x26eb, 0x26ee, 0x26f1, 0x26f5, 0x26f8, - 0x26fb, 0x26fe, 0x2701, 0x2705, 0x2708, 0x270b, 0x270e, 0x2712, 0x2715, 0x2718, 0x271b, 0x271f, - 0x2722, 0x2725, 0x2729, 0x272c, 0x272f, 0x2733, 0x2736, 0x2739, 0x273d, 0x2740, 0x2743, 0x2747, - 0x274a, 0x274d, 0x2751, 0x2754, 0x2758, 0x275b, 0x275e, 0x2762, 0x2765, 0x2769, 0x276c, 0x276f, - 0x2773, 0x2776, 0x277a, 0x277d, 0x2781, 0x2784, 0x2788, 0x278b, 0x278f, 0x2792, 0x2796, 0x2799, - 0x279d, 0x27a0, 0x27a4, 0x27a7, 0x27ab, 0x27ae, 0x27b2, 0x27b5, 0x27b9, 0x27bd, 0x27c0, 0x27c4, - 0x27c7, 0x27cb, 0x27cf, 0x27d2, 0x27d6, 0x27d9, 0x27dd, 0x27e1, 0x27e4, 0x27e8, 0x27ec, 0x27ef, - 0x27f3, 0x27f7, 0x27fa, 0x27fe, 0x2c01, 0x2c03, 0x2c05, 0x2c06, 0x2c08, 0x2c0a, 0x2c0c, 0x2c0e, - 0x2c10, 0x2c12, 0x2c14, 0x2c15, 0x2c17, 0x2c19, 0x2c1b, 0x2c1d, 0x2c1f, 0x2c21, 0x2c23, 0x2c25, - 0x2c27, 0x2c28, 0x2c2a, 0x2c2c, 0x2c2e, 0x2c30, 0x2c32, 0x2c34, 0x2c36, 0x2c38, 0x2c3a, 0x2c3c, - 0x2c3e, 0x2c40, 0x2c42, 0x2c44, 0x2c46, 0x2c48, 0x2c4a, 0x2c4c, 0x2c4e, 0x2c50, 0x2c52, 0x2c54, - 0x2c56, 0x2c58, 0x2c5a, 0x2c5c, 0x2c5e, 0x2c60, 0x2c62, 0x2c64, 0x2c66, 0x2c68, 0x2c6a, 0x2c6c, - 0x2c6e, 0x2c70, 0x2c72, 0x2c74, 0x2c76, 0x2c78, 0x2c7a, 0x2c7c, 0x2c7e, 0x2c80, 0x2c83, 0x2c85, - 0x2c87, 0x2c89, 0x2c8b, 0x2c8d, 0x2c8f, 0x2c91, 0x2c93, 0x2c95, 0x2c98, 0x2c9a, 0x2c9c, 0x2c9e, - 0x2ca0, 0x2ca2, 0x2ca4, 0x2ca6, 0x2ca9, 0x2cab, 0x2cad, 0x2caf, 0x2cb1, 0x2cb3, 0x2cb6, 0x2cb8, - 0x2cba, 0x2cbc, 0x2cbe, 0x2cc1, 0x2cc3, 0x2cc5, 0x2cc7, 0x2cc9, 0x2ccc, 0x2cce, 0x2cd0, 0x2cd2, - 0x2cd4, 0x2cd7, 0x2cd9, 0x2cdb, 0x2cdd, 0x2ce0, 0x2ce2, 0x2ce4, 0x2ce6, 0x2ce9, 0x2ceb, 0x2ced, - 0x2cf0, 0x2cf2, 0x2cf4, 0x2cf6, 0x2cf9, 0x2cfb, 0x2cfd, 0x2d00, 0x2d02, 0x2d04, 0x2d07, 0x2d09, - 0x2d0b, 0x2d0e, 0x2d10, 0x2d12, 0x2d15, 0x2d17, 0x2d19, 0x2d1c, 0x2d1e, 0x2d20, 0x2d23, 0x2d25, - 0x2d28, 0x2d2a, 0x2d2c, 0x2d2f, 0x2d31, 0x2d33, 0x2d36, 0x2d38, 0x2d3b, 0x2d3d, 0x2d40, 0x2d42, - 0x2d44, 0x2d47, 0x2d49, 0x2d4c, 0x2d4e, 0x2d51, 0x2d53, 0x2d56, 0x2d58, 0x2d5a, 0x2d5d, 0x2d5f, - 0x2d62, 0x2d64, 0x2d67, 0x2d69, 0x2d6c, 0x2d6e, 0x2d71, 0x2d73, 0x2d76, 0x2d78, 0x2d7b, 0x2d7e, - 0x2d80, 0x2d83, 0x2d85, 0x2d88, 0x2d8a, 0x2d8d, 0x2d8f, 0x2d92, 0x2d95, 0x2d97, 0x2d9a, 0x2d9c, - 0x2d9f, 0x2da2, 0x2da4, 0x2da7, 0x2da9, 0x2dac, 0x2daf, 0x2db1, 0x2db4, 0x2db6, 0x2db9, 0x2dbc, - 0x2dbe, 0x2dc1, 0x2dc4, 0x2dc6, 0x2dc9, 0x2dcc, 0x2dce, 0x2dd1, 0x2dd4, 0x2dd7, 0x2dd9, 0x2ddc, - 0x2ddf, 0x2de1, 0x2de4, 0x2de7, 0x2dea, 0x2dec, 0x2def, 0x2df2, 0x2df4, 0x2df7, 0x2dfa, 0x2dfd, - 0x2e00, 0x2e02, 0x2e05, 0x2e08, 0x2e0b, 0x2e0d, 0x2e10, 0x2e13, 0x2e16, 0x2e19, 0x2e1c, 0x2e1e, - 0x2e21, 0x2e24, 0x2e27, 0x2e2a, 0x2e2d, 0x2e2f, 0x2e32, 0x2e35, 0x2e38, 0x2e3b, 0x2e3e, 0x2e41, - 0x2e44, 0x2e46, 0x2e49, 0x2e4c, 0x2e4f, 0x2e52, 0x2e55, 0x2e58, 0x2e5b, 0x2e5e, 0x2e61, 0x2e64, - 0x2e67, 0x2e6a, 0x2e6d, 0x2e70, 0x2e73, 0x2e75, 0x2e78, 0x2e7b, 0x2e7e, 0x2e81, 0x2e84, 0x2e87, - 0x2e8b, 0x2e8e, 0x2e91, 0x2e94, 0x2e97, 0x2e9a, 0x2e9d, 0x2ea0, 0x2ea3, 0x2ea6, 0x2ea9, 0x2eac, - 0x2eaf, 0x2eb2, 0x2eb5, 0x2eb8, 0x2ebc, 0x2ebf, 0x2ec2, 0x2ec5, 0x2ec8, 0x2ecb, 0x2ece, 0x2ed1, - 0x2ed5, 0x2ed8, 0x2edb, 0x2ede, 0x2ee1, 0x2ee5, 0x2ee8, 0x2eeb, 0x2eee, 0x2ef1, 0x2ef5, 0x2ef8, - 0x2efb, 0x2efe, 0x2f01, 0x2f05, 0x2f08, 0x2f0b, 0x2f0e, 0x2f12, 0x2f15, 0x2f18, 0x2f1b, 0x2f1f, - 0x2f22, 0x2f25, 0x2f29, 0x2f2c, 0x2f2f, 0x2f33, 0x2f36, 0x2f39, 0x2f3d, 0x2f40, 0x2f43, 0x2f47, - 0x2f4a, 0x2f4d, 0x2f51, 0x2f54, 0x2f58, 0x2f5b, 0x2f5e, 0x2f62, 0x2f65, 0x2f69, 0x2f6c, 0x2f6f, - 0x2f73, 0x2f76, 0x2f7a, 0x2f7d, 0x2f81, 0x2f84, 0x2f88, 0x2f8b, 0x2f8f, 0x2f92, 0x2f96, 0x2f99, - 0x2f9d, 0x2fa0, 0x2fa4, 0x2fa7, 0x2fab, 0x2fae, 0x2fb2, 0x2fb5, 0x2fb9, 0x2fbd, 0x2fc0, 0x2fc4, - 0x2fc7, 0x2fcb, 0x2fcf, 0x2fd2, 0x2fd6, 0x2fd9, 0x2fdd, 0x2fe1, 0x2fe4, 0x2fe8, 0x2fec, 0x2fef, - 0x2ff3, 0x2ff7, 0x2ffa, 0x2ffe, 0x3401, 0x3403, 0x3405, 0x3406, 0x3408, 0x340a, 0x340c, 0x340e, - 0x3410, 0x3412, 0x3414, 0x3415, 0x3417, 0x3419, 0x341b, 0x341d, 0x341f, 0x3421, 0x3423, 0x3425, - 0x3427, 0x3428, 0x342a, 0x342c, 0x342e, 0x3430, 0x3432, 0x3434, 0x3436, 0x3438, 0x343a, 0x343c, - 0x343e, 0x3440, 0x3442, 0x3444, 0x3446, 0x3448, 0x344a, 0x344c, 0x344e, 0x3450, 0x3452, 0x3454, - 0x3456, 0x3458, 0x345a, 0x345c, 0x345e, 0x3460, 0x3462, 0x3464, 0x3466, 0x3468, 0x346a, 0x346c, - 0x346e, 0x3470, 0x3472, 0x3474, 0x3476, 0x3478, 0x347a, 0x347c, 0x347e, 0x3480, 0x3483, 0x3485, - 0x3487, 0x3489, 0x348b, 0x348d, 0x348f, 0x3491, 0x3493, 0x3495, 0x3498, 0x349a, 0x349c, 0x349e, - 0x34a0, 0x34a2, 0x34a4, 0x34a6, 0x34a9, 0x34ab, 0x34ad, 0x34af, 0x34b1, 0x34b3, 0x34b6, 0x34b8, - 0x34ba, 0x34bc, 0x34be, 0x34c1, 0x34c3, 0x34c5, 0x34c7, 0x34c9, 0x34cc, 0x34ce, 0x34d0, 0x34d2, - 0x34d4, 0x34d7, 0x34d9, 0x34db, 0x34dd, 0x34e0, 0x34e2, 0x34e4, 0x34e6, 0x34e9, 0x34eb, 0x34ed, - 0x34f0, 0x34f2, 0x34f4, 0x34f6, 0x34f9, 0x34fb, 0x34fd, 0x3500, 0x3502, 0x3504, 0x3507, 0x3509, - 0x350b, 0x350e, 0x3510, 0x3512, 0x3515, 0x3517, 0x3519, 0x351c, 0x351e, 0x3520, 0x3523, 0x3525, - 0x3528, 0x352a, 0x352c, 0x352f, 0x3531, 0x3533, 0x3536, 0x3538, 0x353b, 0x353d, 0x3540, 0x3542, - 0x3544, 0x3547, 0x3549, 0x354c, 0x354e, 0x3551, 0x3553, 0x3556, 0x3558, 0x355a, 0x355d, 0x355f, - 0x3562, 0x3564, 0x3567, 0x3569, 0x356c, 0x356e, 0x3571, 0x3573, 0x3576, 0x3578, 0x357b, 0x357e, - 0x3580, 0x3583, 0x3585, 0x3588, 0x358a, 0x358d, 0x358f, 0x3592, 0x3595, 0x3597, 0x359a, 0x359c, - 0x359f, 0x35a2, 0x35a4, 0x35a7, 0x35a9, 0x35ac, 0x35af, 0x35b1, 0x35b4, 0x35b6, 0x35b9, 0x35bc, - 0x35be, 0x35c1, 0x35c4, 0x35c6, 0x35c9, 0x35cc, 0x35ce, 0x35d1, 0x35d4, 0x35d7, 0x35d9, 0x35dc, - 0x35df, 0x35e1, 0x35e4, 0x35e7, 0x35ea, 0x35ec, 0x35ef, 0x35f2, 0x35f4, 0x35f7, 0x35fa, 0x35fd, - 0x3600, 0x3602, 0x3605, 0x3608, 0x360b, 0x360d, 0x3610, 0x3613, 0x3616, 0x3619, 0x361c, 0x361e, - 0x3621, 0x3624, 0x3627, 0x362a, 0x362d, 0x362f, 0x3632, 0x3635, 0x3638, 0x363b, 0x363e, 0x3641, - 0x3644, 0x3646, 0x3649, 0x364c, 0x364f, 0x3652, 0x3655, 0x3658, 0x365b, 0x365e, 0x3661, 0x3664, - 0x3667, 0x366a, 0x366d, 0x3670, 0x3673, 0x3675, 0x3678, 0x367b, 0x367e, 0x3681, 0x3684, 0x3687, - 0x368b, 0x368e, 0x3691, 0x3694, 0x3697, 0x369a, 0x369d, 0x36a0, 0x36a3, 0x36a6, 0x36a9, 0x36ac, - 0x36af, 0x36b2, 0x36b5, 0x36b8, 0x36bc, 0x36bf, 0x36c2, 0x36c5, 0x36c8, 0x36cb, 0x36ce, 0x36d1, - 0x36d5, 0x36d8, 0x36db, 0x36de, 0x36e1, 0x36e5, 0x36e8, 0x36eb, 0x36ee, 0x36f1, 0x36f5, 0x36f8, - 0x36fb, 0x36fe, 0x3701, 0x3705, 0x3708, 0x370b, 0x370e, 0x3712, 0x3715, 0x3718, 0x371b, 0x371f, - 0x3722, 0x3725, 0x3729, 0x372c, 0x372f, 0x3733, 0x3736, 0x3739, 0x373d, 0x3740, 0x3743, 0x3747, - 0x374a, 0x374d, 0x3751, 0x3754, 0x3758, 0x375b, 0x375e, 0x3762, 0x3765, 0x3769, 0x376c, 0x376f, - 0x3773, 0x3776, 0x377a, 0x377d, 0x3781, 0x3784, 0x3788, 0x378b, 0x378f, 0x3792, 0x3796, 0x3799, - 0x379d, 0x37a0, 0x37a4, 0x37a7, 0x37ab, 0x37ae, 0x37b2, 0x37b5, 0x37b9, 0x37bd, 0x37c0, 0x37c4, - 0x37c7, 0x37cb, 0x37cf, 0x37d2, 0x37d6, 0x37d9, 0x37dd, 0x37e1, 0x37e4, 0x37e8, 0x37ec, 0x37ef, - 0x37f3, 0x37f7, 0x37fa, 0x37fe, 0x3c01, 0x3c03, 0x3c05, 0x3c06, 0x3c08, 0x3c0a, 0x3c0c, 0x3c0e, - 0x3c10, 0x3c12, 0x3c14, 0x3c15, 0x3c17, 0x3c19, 0x3c1b, 0x3c1d, 0x3c1f, 0x3c21, 0x3c23, 0x3c25, - 0x3c27, 0x3c28, 0x3c2a, 0x3c2c, 0x3c2e, 0x3c30, 0x3c32, 0x3c34, 0x3c36, 0x3c38, 0x3c3a, 0x3c3c, - 0x3c3e, 0x3c40, 0x3c42, 0x3c44, 0x3c46, 0x3c48, 0x3c4a, 0x3c4c, 0x3c4e, 0x3c50, 0x3c52, 0x3c54, - 0x3c56, 0x3c58, 0x3c5a, 0x3c5c, 0x3c5e, 0x3c60, 0x3c62, 0x3c64, 0x3c66, 0x3c68, 0x3c6a, 0x3c6c, - 0x3c6e, 0x3c70, 0x3c72, 0x3c74, 0x3c76, 0x3c78, 0x3c7a, 0x3c7c, 0x3c7e, 0x3c80, 0x3c83, 0x3c85, - 0x3c87, 0x3c89, 0x3c8b, 0x3c8d, 0x3c8f, 0x3c91, 0x3c93, 0x3c95, 0x3c98, 0x3c9a, 0x3c9c, 0x3c9e, - 0x3ca0, 0x3ca2, 0x3ca4, 0x3ca6, 0x3ca9, 0x3cab, 0x3cad, 0x3caf, 0x3cb1, 0x3cb3, 0x3cb6, 0x3cb8, - 0x3cba, 0x3cbc, 0x3cbe, 0x3cc1, 0x3cc3, 0x3cc5, 0x3cc7, 0x3cc9, 0x3ccc, 0x3cce, 0x3cd0, 0x3cd2 -}; - -const uint16_t centTableSSGSquare[3072] = { - 0xee8, 0xee1, 0xeda, 0xed4, 0xecd, 0xec6, 0xebf, 0xeb8, 0xeb1, 0xeab, 0xea4, 0xe9d, - 0xe96, 0xe90, 0xe89, 0xe82, 0xe7c, 0xe75, 0xe6e, 0xe67, 0xe61, 0xe5a, 0xe54, 0xe4d, - 0xe46, 0xe40, 0xe39, 0xe33, 0xe2c, 0xe26, 0xe1f, 0xe18, 0xe12, 0xe0b, 0xe05, 0xdff, - 0xdf8, 0xdf2, 0xdeb, 0xde5, 0xdde, 0xdd8, 0xdd2, 0xdcb, 0xdc5, 0xdbe, 0xdb8, 0xdb2, - 0xdab, 0xda5, 0xd9f, 0xd99, 0xd92, 0xd8c, 0xd86, 0xd7f, 0xd79, 0xd73, 0xd6d, 0xd67, - 0xd60, 0xd5a, 0xd54, 0xd4e, 0xd48, 0xd42, 0xd3c, 0xd35, 0xd2f, 0xd29, 0xd23, 0xd1d, - 0xd17, 0xd11, 0xd0b, 0xd05, 0xcff, 0xcf9, 0xcf3, 0xced, 0xce7, 0xce1, 0xcdb, 0xcd5, - 0xccf, 0xcc9, 0xcc3, 0xcbe, 0xcb8, 0xcb2, 0xcac, 0xca6, 0xca0, 0xc9a, 0xc95, 0xc8f, - 0xc89, 0xc83, 0xc7d, 0xc78, 0xc72, 0xc6c, 0xc66, 0xc61, 0xc5b, 0xc55, 0xc50, 0xc4a, - 0xc44, 0xc3f, 0xc39, 0xc33, 0xc2e, 0xc28, 0xc22, 0xc1d, 0xc17, 0xc12, 0xc0c, 0xc06, - 0xc01, 0xbfb, 0xbf6, 0xbf0, 0xbeb, 0xbe5, 0xbe0, 0xbda, 0xbd5, 0xbcf, 0xbca, 0xbc5, - 0xbbf, 0xbba, 0xbb4, 0xbaf, 0xba9, 0xba4, 0xb9f, 0xb99, 0xb94, 0xb8f, 0xb89, 0xb84, - 0xb7f, 0xb79, 0xb74, 0xb6f, 0xb69, 0xb64, 0xb5f, 0xb5a, 0xb54, 0xb4f, 0xb4a, 0xb45, - 0xb40, 0xb3a, 0xb35, 0xb30, 0xb2b, 0xb26, 0xb21, 0xb1b, 0xb16, 0xb11, 0xb0c, 0xb07, - 0xb02, 0xafd, 0xaf8, 0xaf3, 0xaee, 0xae9, 0xae4, 0xadf, 0xad9, 0xad4, 0xacf, 0xaca, - 0xac6, 0xac1, 0xabc, 0xab7, 0xab2, 0xaad, 0xaa8, 0xaa3, 0xa9e, 0xa99, 0xa94, 0xa8f, - 0xa8a, 0xa86, 0xa81, 0xa7c, 0xa77, 0xa72, 0xa6d, 0xa69, 0xa64, 0xa5f, 0xa5a, 0xa55, - 0xa51, 0xa4c, 0xa47, 0xa42, 0xa3e, 0xa39, 0xa34, 0xa2f, 0xa2b, 0xa26, 0xa21, 0xa1d, - 0xa18, 0xa13, 0xa0f, 0xa0a, 0xa05, 0xa01, 0x9fc, 0x9f8, 0x9f3, 0x9ee, 0x9ea, 0x9e5, - 0x9e1, 0x9dc, 0x9d8, 0x9d3, 0x9ce, 0x9ca, 0x9c5, 0x9c1, 0x9bc, 0x9b8, 0x9b3, 0x9af, - 0x9aa, 0x9a6, 0x9a2, 0x99d, 0x999, 0x994, 0x990, 0x98b, 0x987, 0x983, 0x97e, 0x97a, - 0x975, 0x971, 0x96d, 0x968, 0x964, 0x960, 0x95b, 0x957, 0x953, 0x94e, 0x94a, 0x946, - 0x942, 0x93d, 0x939, 0x935, 0x931, 0x92c, 0x928, 0x924, 0x920, 0x91b, 0x917, 0x913, - 0x90f, 0x90b, 0x906, 0x902, 0x8fe, 0x8fa, 0x8f6, 0x8f2, 0x8ee, 0x8e9, 0x8e5, 0x8e1, - 0x8dd, 0x8d9, 0x8d5, 0x8d1, 0x8cd, 0x8c9, 0x8c5, 0x8c1, 0x8bd, 0x8b9, 0x8b4, 0x8b0, - 0x8ac, 0x8a8, 0x8a4, 0x8a0, 0x89c, 0x899, 0x895, 0x891, 0x88d, 0x889, 0x885, 0x881, - 0x87d, 0x879, 0x875, 0x871, 0x86d, 0x869, 0x865, 0x862, 0x85e, 0x85a, 0x856, 0x852, - 0x84e, 0x84a, 0x847, 0x843, 0x83f, 0x83b, 0x837, 0x834, 0x830, 0x82c, 0x828, 0x825, - 0x821, 0x81d, 0x819, 0x816, 0x812, 0x80e, 0x80a, 0x807, 0x803, 0x7ff, 0x7fc, 0x7f8, - 0x7f4, 0x7f1, 0x7ed, 0x7e9, 0x7e6, 0x7e2, 0x7de, 0x7db, 0x7d7, 0x7d3, 0x7d0, 0x7cc, - 0x7c9, 0x7c5, 0x7c1, 0x7be, 0x7ba, 0x7b7, 0x7b3, 0x7b0, 0x7ac, 0x7a8, 0x7a5, 0x7a1, - 0x79e, 0x79a, 0x797, 0x793, 0x790, 0x78c, 0x789, 0x785, 0x782, 0x77e, 0x77b, 0x778, - 0x774, 0x771, 0x76d, 0x76a, 0x766, 0x763, 0x760, 0x75c, 0x759, 0x755, 0x752, 0x74f, - 0x74b, 0x748, 0x744, 0x741, 0x73e, 0x73a, 0x737, 0x734, 0x730, 0x72d, 0x72a, 0x726, - 0x723, 0x720, 0x71d, 0x719, 0x716, 0x713, 0x70f, 0x70c, 0x709, 0x706, 0x702, 0x6ff, - 0x6fc, 0x6f9, 0x6f6, 0x6f2, 0x6ef, 0x6ec, 0x6e9, 0x6e6, 0x6e2, 0x6df, 0x6dc, 0x6d9, - 0x6d6, 0x6d3, 0x6cf, 0x6cc, 0x6c9, 0x6c6, 0x6c3, 0x6c0, 0x6bd, 0x6ba, 0x6b6, 0x6b3, - 0x6b0, 0x6ad, 0x6aa, 0x6a7, 0x6a4, 0x6a1, 0x69e, 0x69b, 0x698, 0x695, 0x692, 0x68f, - 0x68c, 0x689, 0x685, 0x682, 0x67f, 0x67c, 0x679, 0x676, 0x674, 0x671, 0x66e, 0x66b, - 0x668, 0x665, 0x662, 0x65f, 0x65c, 0x659, 0x656, 0x653, 0x650, 0x64d, 0x64a, 0x647, - 0x644, 0x642, 0x63f, 0x63c, 0x639, 0x636, 0x633, 0x630, 0x62d, 0x62b, 0x628, 0x625, - 0x622, 0x61f, 0x61c, 0x61a, 0x617, 0x614, 0x611, 0x60e, 0x60c, 0x609, 0x606, 0x603, - 0x600, 0x5fe, 0x5fb, 0x5f8, 0x5f5, 0x5f3, 0x5f0, 0x5ed, 0x5ea, 0x5e8, 0x5e5, 0x5e2, - 0x5e0, 0x5dd, 0x5da, 0x5d7, 0x5d5, 0x5d2, 0x5cf, 0x5cd, 0x5ca, 0x5c7, 0x5c5, 0x5c2, - 0x5bf, 0x5bd, 0x5ba, 0x5b7, 0x5b5, 0x5b2, 0x5af, 0x5ad, 0x5aa, 0x5a8, 0x5a5, 0x5a2, - 0x5a0, 0x59d, 0x59b, 0x598, 0x595, 0x593, 0x590, 0x58e, 0x58b, 0x589, 0x586, 0x583, - 0x581, 0x57e, 0x57c, 0x579, 0x577, 0x574, 0x572, 0x56f, 0x56d, 0x56a, 0x568, 0x565, - 0x563, 0x560, 0x55e, 0x55b, 0x559, 0x556, 0x554, 0x551, 0x54f, 0x54d, 0x54a, 0x548, - 0x545, 0x543, 0x540, 0x53e, 0x53c, 0x539, 0x537, 0x534, 0x532, 0x52f, 0x52d, 0x52b, - 0x528, 0x526, 0x524, 0x521, 0x51f, 0x51c, 0x51a, 0x518, 0x515, 0x513, 0x511, 0x50e, - 0x50c, 0x50a, 0x507, 0x505, 0x503, 0x500, 0x4fe, 0x4fc, 0x4f9, 0x4f7, 0x4f5, 0x4f3, - 0x4f0, 0x4ee, 0x4ec, 0x4e9, 0x4e7, 0x4e5, 0x4e3, 0x4e0, 0x4de, 0x4dc, 0x4da, 0x4d7, - 0x4d5, 0x4d3, 0x4d1, 0x4cf, 0x4cc, 0x4ca, 0x4c8, 0x4c6, 0x4c3, 0x4c1, 0x4bf, 0x4bd, - 0x4bb, 0x4b9, 0x4b6, 0x4b4, 0x4b2, 0x4b0, 0x4ae, 0x4ac, 0x4a9, 0x4a7, 0x4a5, 0x4a3, - 0x4a1, 0x49f, 0x49d, 0x49a, 0x498, 0x496, 0x494, 0x492, 0x490, 0x48e, 0x48c, 0x489, - 0x487, 0x485, 0x483, 0x481, 0x47f, 0x47d, 0x47b, 0x479, 0x477, 0x475, 0x473, 0x471, - 0x46f, 0x46c, 0x46a, 0x468, 0x466, 0x464, 0x462, 0x460, 0x45e, 0x45c, 0x45a, 0x458, - 0x456, 0x454, 0x452, 0x450, 0x44e, 0x44c, 0x44a, 0x448, 0x446, 0x444, 0x442, 0x440, - 0x43e, 0x43c, 0x43b, 0x439, 0x437, 0x435, 0x433, 0x431, 0x42f, 0x42d, 0x42b, 0x429, - 0x427, 0x425, 0x423, 0x421, 0x420, 0x41e, 0x41c, 0x41a, 0x418, 0x416, 0x414, 0x412, - 0x410, 0x40f, 0x40d, 0x40b, 0x409, 0x407, 0x405, 0x403, 0x401, 0x400, 0x3fe, 0x3fc, - 0x3fa, 0x3f8, 0x3f6, 0x3f5, 0x3f3, 0x3f1, 0x3ef, 0x3ed, 0x3eb, 0x3ea, 0x3e8, 0x3e6, - 0x3e4, 0x3e2, 0x3e1, 0x3df, 0x3dd, 0x3db, 0x3da, 0x3d8, 0x3d6, 0x3d4, 0x3d2, 0x3d1, - 0x3cf, 0x3cd, 0x3cb, 0x3ca, 0x3c8, 0x3c6, 0x3c4, 0x3c3, 0x3c1, 0x3bf, 0x3bd, 0x3bc, - 0x3ba, 0x3b8, 0x3b7, 0x3b5, 0x3b3, 0x3b1, 0x3b0, 0x3ae, 0x3ac, 0x3ab, 0x3a9, 0x3a7, - 0x3a6, 0x3a4, 0x3a2, 0x3a1, 0x39f, 0x39d, 0x39c, 0x39a, 0x398, 0x397, 0x395, 0x393, - 0x392, 0x390, 0x38e, 0x38d, 0x38b, 0x389, 0x388, 0x386, 0x384, 0x383, 0x381, 0x380, - 0x37e, 0x37c, 0x37b, 0x379, 0x378, 0x376, 0x374, 0x373, 0x371, 0x370, 0x36e, 0x36c, - 0x36b, 0x369, 0x368, 0x366, 0x365, 0x363, 0x361, 0x360, 0x35e, 0x35d, 0x35b, 0x35a, - 0x358, 0x357, 0x355, 0x353, 0x352, 0x350, 0x34f, 0x34d, 0x34c, 0x34a, 0x349, 0x347, - 0x346, 0x344, 0x343, 0x341, 0x340, 0x33e, 0x33d, 0x33b, 0x33a, 0x338, 0x337, 0x335, - 0x334, 0x332, 0x331, 0x32f, 0x32e, 0x32c, 0x32b, 0x32a, 0x328, 0x327, 0x325, 0x324, - 0x322, 0x321, 0x31f, 0x31e, 0x31c, 0x31b, 0x31a, 0x318, 0x317, 0x315, 0x314, 0x312, - 0x311, 0x310, 0x30e, 0x30d, 0x30b, 0x30a, 0x309, 0x307, 0x306, 0x304, 0x303, 0x302, - 0x300, 0x2ff, 0x2fd, 0x2fc, 0x2fb, 0x2f9, 0x2f8, 0x2f7, 0x2f5, 0x2f4, 0x2f2, 0x2f1, - 0x2f0, 0x2ee, 0x2ed, 0x2ec, 0x2ea, 0x2e9, 0x2e8, 0x2e6, 0x2e5, 0x2e4, 0x2e2, 0x2e1, - 0x2e0, 0x2de, 0x2dd, 0x2dc, 0x2da, 0x2d9, 0x2d8, 0x2d6, 0x2d5, 0x2d4, 0x2d3, 0x2d1, - 0x2d0, 0x2cf, 0x2cd, 0x2cc, 0x2cb, 0x2c9, 0x2c8, 0x2c7, 0x2c6, 0x2c4, 0x2c3, 0x2c2, - 0x2c0, 0x2bf, 0x2be, 0x2bd, 0x2bb, 0x2ba, 0x2b9, 0x2b8, 0x2b6, 0x2b5, 0x2b4, 0x2b3, - 0x2b1, 0x2b0, 0x2af, 0x2ae, 0x2ac, 0x2ab, 0x2aa, 0x2a9, 0x2a7, 0x2a6, 0x2a5, 0x2a4, - 0x2a3, 0x2a1, 0x2a0, 0x29f, 0x29e, 0x29d, 0x29b, 0x29a, 0x299, 0x298, 0x297, 0x295, - 0x294, 0x293, 0x292, 0x291, 0x28f, 0x28e, 0x28d, 0x28c, 0x28b, 0x28a, 0x288, 0x287, - 0x286, 0x285, 0x284, 0x283, 0x281, 0x280, 0x27f, 0x27e, 0x27d, 0x27c, 0x27a, 0x279, - 0x278, 0x277, 0x276, 0x275, 0x274, 0x272, 0x271, 0x270, 0x26f, 0x26e, 0x26d, 0x26c, - 0x26b, 0x26a, 0x268, 0x267, 0x266, 0x265, 0x264, 0x263, 0x262, 0x261, 0x260, 0x25e, - 0x25d, 0x25c, 0x25b, 0x25a, 0x259, 0x258, 0x257, 0x256, 0x255, 0x254, 0x253, 0x251, - 0x250, 0x24f, 0x24e, 0x24d, 0x24c, 0x24b, 0x24a, 0x249, 0x248, 0x247, 0x246, 0x245, - 0x244, 0x243, 0x242, 0x241, 0x240, 0x23e, 0x23d, 0x23c, 0x23b, 0x23a, 0x239, 0x238, - 0x237, 0x236, 0x235, 0x234, 0x233, 0x232, 0x231, 0x230, 0x22f, 0x22e, 0x22d, 0x22c, - 0x22b, 0x22a, 0x229, 0x228, 0x227, 0x226, 0x225, 0x224, 0x223, 0x222, 0x221, 0x220, - 0x21f, 0x21e, 0x21d, 0x21c, 0x21b, 0x21a, 0x219, 0x218, 0x217, 0x216, 0x216, 0x215, - 0x214, 0x213, 0x212, 0x211, 0x210, 0x20f, 0x20e, 0x20d, 0x20c, 0x20b, 0x20a, 0x209, - 0x208, 0x207, 0x206, 0x205, 0x204, 0x204, 0x203, 0x202, 0x201, 0x200, 0x1ff, 0x1fe, - 0x1fd, 0x1fc, 0x1fb, 0x1fa, 0x1f9, 0x1f8, 0x1f8, 0x1f7, 0x1f6, 0x1f5, 0x1f4, 0x1f3, - 0x1f2, 0x1f1, 0x1f0, 0x1ef, 0x1ef, 0x1ee, 0x1ed, 0x1ec, 0x1eb, 0x1ea, 0x1e9, 0x1e8, - 0x1e7, 0x1e7, 0x1e6, 0x1e5, 0x1e4, 0x1e3, 0x1e2, 0x1e1, 0x1e0, 0x1e0, 0x1df, 0x1de, - 0x1dd, 0x1dc, 0x1db, 0x1da, 0x1da, 0x1d9, 0x1d8, 0x1d7, 0x1d6, 0x1d5, 0x1d4, 0x1d4, - 0x1d3, 0x1d2, 0x1d1, 0x1d0, 0x1cf, 0x1cf, 0x1ce, 0x1cd, 0x1cc, 0x1cb, 0x1ca, 0x1ca, - 0x1c9, 0x1c8, 0x1c7, 0x1c6, 0x1c6, 0x1c5, 0x1c4, 0x1c3, 0x1c2, 0x1c1, 0x1c1, 0x1c0, - 0x1bf, 0x1be, 0x1bd, 0x1bd, 0x1bc, 0x1bb, 0x1ba, 0x1b9, 0x1b9, 0x1b8, 0x1b7, 0x1b6, - 0x1b5, 0x1b5, 0x1b4, 0x1b3, 0x1b2, 0x1b1, 0x1b1, 0x1b0, 0x1af, 0x1ae, 0x1ae, 0x1ad, - 0x1ac, 0x1ab, 0x1ab, 0x1aa, 0x1a9, 0x1a8, 0x1a7, 0x1a7, 0x1a6, 0x1a5, 0x1a4, 0x1a4, - 0x1a3, 0x1a2, 0x1a1, 0x1a1, 0x1a0, 0x19f, 0x19e, 0x19e, 0x19d, 0x19c, 0x19b, 0x19b, - 0x19a, 0x199, 0x198, 0x198, 0x197, 0x196, 0x195, 0x195, 0x194, 0x193, 0x193, 0x192, - 0x191, 0x190, 0x190, 0x18f, 0x18e, 0x18e, 0x18d, 0x18c, 0x18b, 0x18b, 0x18a, 0x189, - 0x189, 0x188, 0x187, 0x186, 0x186, 0x185, 0x184, 0x184, 0x183, 0x182, 0x182, 0x181, - 0x180, 0x17f, 0x17f, 0x17e, 0x17d, 0x17d, 0x17c, 0x17b, 0x17b, 0x17a, 0x179, 0x179, - 0x178, 0x177, 0x177, 0x176, 0x175, 0x175, 0x174, 0x173, 0x172, 0x172, 0x171, 0x170, - 0x170, 0x16f, 0x16f, 0x16e, 0x16d, 0x16d, 0x16c, 0x16b, 0x16b, 0x16a, 0x169, 0x169, - 0x168, 0x167, 0x167, 0x166, 0x165, 0x165, 0x164, 0x163, 0x163, 0x162, 0x162, 0x161, - 0x160, 0x160, 0x15f, 0x15e, 0x15e, 0x15d, 0x15c, 0x15c, 0x15b, 0x15b, 0x15a, 0x159, - 0x159, 0x158, 0x157, 0x157, 0x156, 0x156, 0x155, 0x154, 0x154, 0x153, 0x153, 0x152, - 0x151, 0x151, 0x150, 0x14f, 0x14f, 0x14e, 0x14e, 0x14d, 0x14c, 0x14c, 0x14b, 0x14b, - 0x14a, 0x149, 0x149, 0x148, 0x148, 0x147, 0x147, 0x146, 0x145, 0x145, 0x144, 0x144, - 0x143, 0x142, 0x142, 0x141, 0x141, 0x140, 0x140, 0x13f, 0x13e, 0x13e, 0x13d, 0x13d, - 0x13c, 0x13c, 0x13b, 0x13a, 0x13a, 0x139, 0x139, 0x138, 0x138, 0x137, 0x136, 0x136, - 0x135, 0x135, 0x134, 0x134, 0x133, 0x133, 0x132, 0x131, 0x131, 0x130, 0x130, 0x12f, - 0x12f, 0x12e, 0x12e, 0x12d, 0x12d, 0x12c, 0x12b, 0x12b, 0x12a, 0x12a, 0x129, 0x129, - 0x128, 0x128, 0x127, 0x127, 0x126, 0x126, 0x125, 0x124, 0x124, 0x123, 0x123, 0x122, - 0x122, 0x121, 0x121, 0x120, 0x120, 0x11f, 0x11f, 0x11e, 0x11e, 0x11d, 0x11d, 0x11c, - 0x11c, 0x11b, 0x11b, 0x11a, 0x11a, 0x119, 0x119, 0x118, 0x118, 0x117, 0x117, 0x116, - 0x116, 0x115, 0x115, 0x114, 0x114, 0x113, 0x113, 0x112, 0x112, 0x111, 0x111, 0x110, - 0x110, 0x10f, 0x10f, 0x10e, 0x10e, 0x10d, 0x10d, 0x10c, 0x10c, 0x10b, 0x10b, 0x10a, - 0x10a, 0x109, 0x109, 0x108, 0x108, 0x107, 0x107, 0x106, 0x106, 0x106, 0x105, 0x105, - 0x104, 0x104, 0x103, 0x103, 0x102, 0x102, 0x101, 0x101, 0x100, 0x100, 0x0ff, 0x0ff, - 0x0ff, 0x0fe, 0x0fe, 0x0fd, 0x0fd, 0x0fc, 0x0fc, 0x0fb, 0x0fb, 0x0fa, 0x0fa, 0x0fa, - 0x0f9, 0x0f9, 0x0f8, 0x0f8, 0x0f7, 0x0f7, 0x0f6, 0x0f6, 0x0f5, 0x0f5, 0x0f5, 0x0f4, - 0x0f4, 0x0f3, 0x0f3, 0x0f2, 0x0f2, 0x0f2, 0x0f1, 0x0f1, 0x0f0, 0x0f0, 0x0ef, 0x0ef, - 0x0ef, 0x0ee, 0x0ee, 0x0ed, 0x0ed, 0x0ec, 0x0ec, 0x0ec, 0x0eb, 0x0eb, 0x0ea, 0x0ea, - 0x0e9, 0x0e9, 0x0e9, 0x0e8, 0x0e8, 0x0e7, 0x0e7, 0x0e6, 0x0e6, 0x0e6, 0x0e5, 0x0e5, - 0x0e4, 0x0e4, 0x0e4, 0x0e3, 0x0e3, 0x0e2, 0x0e2, 0x0e2, 0x0e1, 0x0e1, 0x0e0, 0x0e0, - 0x0e0, 0x0df, 0x0df, 0x0de, 0x0de, 0x0dd, 0x0dd, 0x0dd, 0x0dc, 0x0dc, 0x0dc, 0x0db, - 0x0db, 0x0da, 0x0da, 0x0da, 0x0d9, 0x0d9, 0x0d8, 0x0d8, 0x0d8, 0x0d7, 0x0d7, 0x0d6, - 0x0d6, 0x0d6, 0x0d5, 0x0d5, 0x0d4, 0x0d4, 0x0d4, 0x0d3, 0x0d3, 0x0d3, 0x0d2, 0x0d2, - 0x0d1, 0x0d1, 0x0d1, 0x0d0, 0x0d0, 0x0d0, 0x0cf, 0x0cf, 0x0ce, 0x0ce, 0x0ce, 0x0cd, - 0x0cd, 0x0cd, 0x0cc, 0x0cc, 0x0cb, 0x0cb, 0x0cb, 0x0ca, 0x0ca, 0x0ca, 0x0c9, 0x0c9, - 0x0c9, 0x0c8, 0x0c8, 0x0c7, 0x0c7, 0x0c7, 0x0c6, 0x0c6, 0x0c6, 0x0c5, 0x0c5, 0x0c5, - 0x0c4, 0x0c4, 0x0c4, 0x0c3, 0x0c3, 0x0c3, 0x0c2, 0x0c2, 0x0c1, 0x0c1, 0x0c1, 0x0c0, - 0x0c0, 0x0c0, 0x0bf, 0x0bf, 0x0bf, 0x0be, 0x0be, 0x0be, 0x0bd, 0x0bd, 0x0bd, 0x0bc, - 0x0bc, 0x0bc, 0x0bb, 0x0bb, 0x0bb, 0x0ba, 0x0ba, 0x0ba, 0x0b9, 0x0b9, 0x0b9, 0x0b8, - 0x0b8, 0x0b8, 0x0b7, 0x0b7, 0x0b7, 0x0b6, 0x0b6, 0x0b6, 0x0b5, 0x0b5, 0x0b5, 0x0b4, - 0x0b4, 0x0b4, 0x0b3, 0x0b3, 0x0b3, 0x0b2, 0x0b2, 0x0b2, 0x0b1, 0x0b1, 0x0b1, 0x0b0, - 0x0b0, 0x0b0, 0x0af, 0x0af, 0x0af, 0x0af, 0x0ae, 0x0ae, 0x0ae, 0x0ad, 0x0ad, 0x0ad, - 0x0ac, 0x0ac, 0x0ac, 0x0ab, 0x0ab, 0x0ab, 0x0aa, 0x0aa, 0x0aa, 0x0aa, 0x0a9, 0x0a9, - 0x0a9, 0x0a8, 0x0a8, 0x0a8, 0x0a7, 0x0a7, 0x0a7, 0x0a7, 0x0a6, 0x0a6, 0x0a6, 0x0a5, - 0x0a5, 0x0a5, 0x0a4, 0x0a4, 0x0a4, 0x0a4, 0x0a3, 0x0a3, 0x0a3, 0x0a2, 0x0a2, 0x0a2, - 0x0a2, 0x0a1, 0x0a1, 0x0a1, 0x0a0, 0x0a0, 0x0a0, 0x09f, 0x09f, 0x09f, 0x09f, 0x09e, - 0x09e, 0x09e, 0x09d, 0x09d, 0x09d, 0x09d, 0x09c, 0x09c, 0x09c, 0x09b, 0x09b, 0x09b, - 0x09b, 0x09a, 0x09a, 0x09a, 0x09a, 0x099, 0x099, 0x099, 0x098, 0x098, 0x098, 0x098, - 0x097, 0x097, 0x097, 0x097, 0x096, 0x096, 0x096, 0x095, 0x095, 0x095, 0x095, 0x094, - 0x094, 0x094, 0x094, 0x093, 0x093, 0x093, 0x093, 0x092, 0x092, 0x092, 0x091, 0x091, - 0x091, 0x091, 0x090, 0x090, 0x090, 0x090, 0x08f, 0x08f, 0x08f, 0x08f, 0x08e, 0x08e, - 0x08e, 0x08e, 0x08d, 0x08d, 0x08d, 0x08d, 0x08c, 0x08c, 0x08c, 0x08c, 0x08b, 0x08b, - 0x08b, 0x08b, 0x08a, 0x08a, 0x08a, 0x08a, 0x089, 0x089, 0x089, 0x089, 0x088, 0x088, - 0x088, 0x088, 0x087, 0x087, 0x087, 0x087, 0x086, 0x086, 0x086, 0x086, 0x085, 0x085, - 0x085, 0x085, 0x084, 0x084, 0x084, 0x084, 0x083, 0x083, 0x083, 0x083, 0x083, 0x082, - 0x082, 0x082, 0x082, 0x081, 0x081, 0x081, 0x081, 0x080, 0x080, 0x080, 0x080, 0x07f, - 0x07f, 0x07f, 0x07f, 0x07f, 0x07e, 0x07e, 0x07e, 0x07e, 0x07d, 0x07d, 0x07d, 0x07d, - 0x07d, 0x07c, 0x07c, 0x07c, 0x07c, 0x07b, 0x07b, 0x07b, 0x07b, 0x07b, 0x07a, 0x07a, - 0x07a, 0x07a, 0x079, 0x079, 0x079, 0x079, 0x079, 0x078, 0x078, 0x078, 0x078, 0x077, - 0x077, 0x077, 0x077, 0x077, 0x076, 0x076, 0x076, 0x076, 0x076, 0x075, 0x075, 0x075, - 0x075, 0x074, 0x074, 0x074, 0x074, 0x074, 0x073, 0x073, 0x073, 0x073, 0x073, 0x072, - 0x072, 0x072, 0x072, 0x072, 0x071, 0x071, 0x071, 0x071, 0x071, 0x070, 0x070, 0x070, - 0x070, 0x070, 0x06f, 0x06f, 0x06f, 0x06f, 0x06f, 0x06e, 0x06e, 0x06e, 0x06e, 0x06e, - 0x06d, 0x06d, 0x06d, 0x06d, 0x06d, 0x06c, 0x06c, 0x06c, 0x06c, 0x06c, 0x06b, 0x06b, - 0x06b, 0x06b, 0x06b, 0x06a, 0x06a, 0x06a, 0x06a, 0x06a, 0x069, 0x069, 0x069, 0x069, - 0x069, 0x069, 0x068, 0x068, 0x068, 0x068, 0x068, 0x067, 0x067, 0x067, 0x067, 0x067, - 0x066, 0x066, 0x066, 0x066, 0x066, 0x066, 0x065, 0x065, 0x065, 0x065, 0x065, 0x064, - 0x064, 0x064, 0x064, 0x064, 0x064, 0x063, 0x063, 0x063, 0x063, 0x063, 0x062, 0x062, - 0x062, 0x062, 0x062, 0x062, 0x061, 0x061, 0x061, 0x061, 0x061, 0x061, 0x060, 0x060, - 0x060, 0x060, 0x060, 0x060, 0x05f, 0x05f, 0x05f, 0x05f, 0x05f, 0x05e, 0x05e, 0x05e, - 0x05e, 0x05e, 0x05e, 0x05d, 0x05d, 0x05d, 0x05d, 0x05d, 0x05d, 0x05c, 0x05c, 0x05c, - 0x05c, 0x05c, 0x05c, 0x05b, 0x05b, 0x05b, 0x05b, 0x05b, 0x05b, 0x05a, 0x05a, 0x05a, - 0x05a, 0x05a, 0x05a, 0x05a, 0x059, 0x059, 0x059, 0x059, 0x059, 0x059, 0x058, 0x058, - 0x058, 0x058, 0x058, 0x058, 0x057, 0x057, 0x057, 0x057, 0x057, 0x057, 0x056, 0x056, - 0x056, 0x056, 0x056, 0x056, 0x056, 0x055, 0x055, 0x055, 0x055, 0x055, 0x055, 0x054, - 0x054, 0x054, 0x054, 0x054, 0x054, 0x054, 0x053, 0x053, 0x053, 0x053, 0x053, 0x053, - 0x053, 0x052, 0x052, 0x052, 0x052, 0x052, 0x052, 0x051, 0x051, 0x051, 0x051, 0x051, - 0x051, 0x051, 0x050, 0x050, 0x050, 0x050, 0x050, 0x050, 0x050, 0x04f, 0x04f, 0x04f, - 0x04f, 0x04f, 0x04f, 0x04f, 0x04e, 0x04e, 0x04e, 0x04e, 0x04e, 0x04e, 0x04e, 0x04d, - 0x04d, 0x04d, 0x04d, 0x04d, 0x04d, 0x04d, 0x04c, 0x04c, 0x04c, 0x04c, 0x04c, 0x04c, - 0x04c, 0x04c, 0x04b, 0x04b, 0x04b, 0x04b, 0x04b, 0x04b, 0x04b, 0x04a, 0x04a, 0x04a, - 0x04a, 0x04a, 0x04a, 0x04a, 0x04a, 0x049, 0x049, 0x049, 0x049, 0x049, 0x049, 0x049, - 0x048, 0x048, 0x048, 0x048, 0x048, 0x048, 0x048, 0x048, 0x047, 0x047, 0x047, 0x047, - 0x047, 0x047, 0x047, 0x047, 0x046, 0x046, 0x046, 0x046, 0x046, 0x046, 0x046, 0x046, - 0x045, 0x045, 0x045, 0x045, 0x045, 0x045, 0x045, 0x045, 0x044, 0x044, 0x044, 0x044, - 0x044, 0x044, 0x044, 0x044, 0x043, 0x043, 0x043, 0x043, 0x043, 0x043, 0x043, 0x043, - 0x042, 0x042, 0x042, 0x042, 0x042, 0x042, 0x042, 0x042, 0x041, 0x041, 0x041, 0x041, - 0x041, 0x041, 0x041, 0x041, 0x041, 0x040, 0x040, 0x040, 0x040, 0x040, 0x040, 0x040, - 0x040, 0x040, 0x03f, 0x03f, 0x03f, 0x03f, 0x03f, 0x03f, 0x03f, 0x03f, 0x03e, 0x03e, - 0x03e, 0x03e, 0x03e, 0x03e, 0x03e, 0x03e, 0x03e, 0x03d, 0x03d, 0x03d, 0x03d, 0x03d, - 0x03d, 0x03d, 0x03d, 0x03d, 0x03c, 0x03c, 0x03c, 0x03c, 0x03c, 0x03c, 0x03c, 0x03c, - 0x03c, 0x03c, 0x03b, 0x03b, 0x03b, 0x03b, 0x03b, 0x03b, 0x03b, 0x03b, 0x03b, 0x03a, - 0x03a, 0x03a, 0x03a, 0x03a, 0x03a, 0x03a, 0x03a, 0x03a, 0x03a, 0x039, 0x039, 0x039, - 0x039, 0x039, 0x039, 0x039, 0x039, 0x039, 0x038, 0x038, 0x038, 0x038, 0x038, 0x038, - 0x038, 0x038, 0x038, 0x038, 0x037, 0x037, 0x037, 0x037, 0x037, 0x037, 0x037, 0x037, - 0x037, 0x037, 0x036, 0x036, 0x036, 0x036, 0x036, 0x036, 0x036, 0x036, 0x036, 0x036, - 0x036, 0x035, 0x035, 0x035, 0x035, 0x035, 0x035, 0x035, 0x035, 0x035, 0x035, 0x034, - 0x034, 0x034, 0x034, 0x034, 0x034, 0x034, 0x034, 0x034, 0x034, 0x034, 0x033, 0x033, - 0x033, 0x033, 0x033, 0x033, 0x033, 0x033, 0x033, 0x033, 0x033, 0x032, 0x032, 0x032, - 0x032, 0x032, 0x032, 0x032, 0x032, 0x032, 0x032, 0x032, 0x031, 0x031, 0x031, 0x031, - 0x031, 0x031, 0x031, 0x031, 0x031, 0x031, 0x031, 0x030, 0x030, 0x030, 0x030, 0x030, - 0x030, 0x030, 0x030, 0x030, 0x030, 0x030, 0x030, 0x02f, 0x02f, 0x02f, 0x02f, 0x02f, - 0x02f, 0x02f, 0x02f, 0x02f, 0x02f, 0x02f, 0x02e, 0x02e, 0x02e, 0x02e, 0x02e, 0x02e, - 0x02e, 0x02e, 0x02e, 0x02e, 0x02e, 0x02e, 0x02d, 0x02d, 0x02d, 0x02d, 0x02d, 0x02d, - 0x02d, 0x02d, 0x02d, 0x02d, 0x02d, 0x02d, 0x02d, 0x02c, 0x02c, 0x02c, 0x02c, 0x02c, - 0x02c, 0x02c, 0x02c, 0x02c, 0x02c, 0x02c, 0x02c, 0x02b, 0x02b, 0x02b, 0x02b, 0x02b, - 0x02b, 0x02b, 0x02b, 0x02b, 0x02b, 0x02b, 0x02b, 0x02b, 0x02a, 0x02a, 0x02a, 0x02a, - 0x02a, 0x02a, 0x02a, 0x02a, 0x02a, 0x02a, 0x02a, 0x02a, 0x02a, 0x029, 0x029, 0x029, - 0x029, 0x029, 0x029, 0x029, 0x029, 0x029, 0x029, 0x029, 0x029, 0x029, 0x029, 0x028, - 0x028, 0x028, 0x028, 0x028, 0x028, 0x028, 0x028, 0x028, 0x028, 0x028, 0x028, 0x028, - 0x028, 0x027, 0x027, 0x027, 0x027, 0x027, 0x027, 0x027, 0x027, 0x027, 0x027, 0x027, - 0x027, 0x027, 0x027, 0x026, 0x026, 0x026, 0x026, 0x026, 0x026, 0x026, 0x026, 0x026, - 0x026, 0x026, 0x026, 0x026, 0x026, 0x026, 0x025, 0x025, 0x025, 0x025, 0x025, 0x025, - 0x025, 0x025, 0x025, 0x025, 0x025, 0x025, 0x025, 0x025, 0x025, 0x024, 0x024, 0x024, - 0x024, 0x024, 0x024, 0x024, 0x024, 0x024, 0x024, 0x024, 0x024, 0x024, 0x024, 0x024, - 0x023, 0x023, 0x023, 0x023, 0x023, 0x023, 0x023, 0x023, 0x023, 0x023, 0x023, 0x023, - 0x023, 0x023, 0x023, 0x023, 0x022, 0x022, 0x022, 0x022, 0x022, 0x022, 0x022, 0x022, - 0x022, 0x022, 0x022, 0x022, 0x022, 0x022, 0x022, 0x022, 0x021, 0x021, 0x021, 0x021, - 0x021, 0x021, 0x021, 0x021, 0x021, 0x021, 0x021, 0x021, 0x021, 0x021, 0x021, 0x021, - 0x021, 0x020, 0x020, 0x020, 0x020, 0x020, 0x020, 0x020, 0x020, 0x020, 0x020, 0x020, - 0x020, 0x020, 0x020, 0x020, 0x020, 0x020, 0x01f, 0x01f, 0x01f, 0x01f, 0x01f, 0x01f, - 0x01f, 0x01f, 0x01f, 0x01f, 0x01f, 0x01f, 0x01f, 0x01f, 0x01f, 0x01f, 0x01f, 0x01f, - 0x01e, 0x01e, 0x01e, 0x01e, 0x01e, 0x01e, 0x01e, 0x01e, 0x01e, 0x01e, 0x01e, 0x01e, - 0x01e, 0x01e, 0x01e, 0x01e, 0x01e, 0x01e, 0x01e, 0x01d, 0x01d, 0x01d, 0x01d, 0x01d, - 0x01d, 0x01d, 0x01d, 0x01d, 0x01d, 0x01d, 0x01d, 0x01d, 0x01d, 0x01d, 0x01d, 0x01d, - 0x01d, 0x01d, 0x01c, 0x01c, 0x01c, 0x01c, 0x01c, 0x01c, 0x01c, 0x01c, 0x01c, 0x01c, - 0x01c, 0x01c, 0x01c, 0x01c, 0x01c, 0x01c, 0x01c, 0x01c, 0x01c, 0x01b, 0x01b, 0x01b, - 0x01b, 0x01b, 0x01b, 0x01b, 0x01b, 0x01b, 0x01b, 0x01b, 0x01b, 0x01b, 0x01b, 0x01b, - 0x01b, 0x01b, 0x01b, 0x01b, 0x01b, 0x01b, 0x01a, 0x01a, 0x01a, 0x01a, 0x01a, 0x01a, - 0x01a, 0x01a, 0x01a, 0x01a, 0x01a, 0x01a, 0x01a, 0x01a, 0x01a, 0x01a, 0x01a, 0x01a, - 0x01a, 0x01a, 0x01a, 0x019, 0x019, 0x019, 0x019, 0x019, 0x019, 0x019, 0x019, 0x019, - 0x019, 0x019, 0x019, 0x019, 0x019, 0x019, 0x019, 0x019, 0x019, 0x019, 0x019, 0x019, - 0x019, 0x018, 0x018, 0x018, 0x018, 0x018, 0x018, 0x018, 0x018, 0x018, 0x018, 0x018, - 0x018, 0x018, 0x018, 0x018, 0x018, 0x018, 0x018, 0x018, 0x018, 0x018, 0x018, 0x018, - 0x018, 0x017, 0x017, 0x017, 0x017, 0x017, 0x017, 0x017, 0x017, 0x017, 0x017, 0x017, - 0x017, 0x017, 0x017, 0x017, 0x017, 0x017, 0x017, 0x017, 0x017, 0x017, 0x017, 0x017, - 0x017, 0x016, 0x016, 0x016, 0x016, 0x016, 0x016, 0x016, 0x016, 0x016, 0x016, 0x016, - 0x016, 0x016, 0x016, 0x016, 0x016, 0x016, 0x016, 0x016, 0x016, 0x016, 0x016, 0x016, - 0x016, 0x016, 0x015, 0x015, 0x015, 0x015, 0x015, 0x015, 0x015, 0x015, 0x015, 0x015, - 0x015, 0x015, 0x015, 0x015, 0x015, 0x015, 0x015, 0x015, 0x015, 0x015, 0x015, 0x015, - 0x015, 0x015, 0x015, 0x015, 0x014, 0x014, 0x014, 0x014, 0x014, 0x014, 0x014, 0x014, - 0x014, 0x014, 0x014, 0x014, 0x014, 0x014, 0x014, 0x014, 0x014, 0x014, 0x014, 0x014, - 0x014, 0x014, 0x014, 0x014, 0x014, 0x014, 0x014, 0x014, 0x013, 0x013, 0x013, 0x013, - 0x013, 0x013, 0x013, 0x013, 0x013, 0x013, 0x013, 0x013, 0x013, 0x013, 0x013, 0x013, - 0x013, 0x013, 0x013, 0x013, 0x013, 0x013, 0x013, 0x013, 0x013, 0x013, 0x013, 0x013, - 0x013, 0x012, 0x012, 0x012, 0x012, 0x012, 0x012, 0x012, 0x012, 0x012, 0x012, 0x012, - 0x012, 0x012, 0x012, 0x012, 0x012, 0x012, 0x012, 0x012, 0x012, 0x012, 0x012, 0x012, - 0x012, 0x012, 0x012, 0x012, 0x012, 0x012, 0x012, 0x012, 0x011, 0x011, 0x011, 0x011, - 0x011, 0x011, 0x011, 0x011, 0x011, 0x011, 0x011, 0x011, 0x011, 0x011, 0x011, 0x011, - 0x011, 0x011, 0x011, 0x011, 0x011, 0x011, 0x011, 0x011, 0x011, 0x011, 0x011, 0x011, - 0x011, 0x011, 0x011, 0x011, 0x011, 0x010, 0x010, 0x010, 0x010, 0x010, 0x010, 0x010, - 0x010, 0x010, 0x010, 0x010, 0x010, 0x010, 0x010, 0x010, 0x010, 0x010, 0x010, 0x010, - 0x010, 0x010, 0x010, 0x010, 0x010, 0x010, 0x010, 0x010, 0x010, 0x010, 0x010, 0x010, - 0x010, 0x010, 0x010, 0x00f, 0x00f, 0x00f, 0x00f, 0x00f, 0x00f, 0x00f, 0x00f, 0x00f, - 0x00f, 0x00f, 0x00f, 0x00f, 0x00f, 0x00f, 0x00f, 0x00f, 0x00f, 0x00f, 0x00f, 0x00f -}; - -const uint16_t centTableSSGTriangle[3072] = { - 0x077, 0x077, 0x077, 0x077, 0x076, 0x076, 0x076, 0x076, 0x076, 0x075, 0x075, 0x075, - 0x075, 0x074, 0x074, 0x074, 0x074, 0x074, 0x073, 0x073, 0x073, 0x073, 0x073, 0x072, - 0x072, 0x072, 0x072, 0x072, 0x071, 0x071, 0x071, 0x071, 0x071, 0x070, 0x070, 0x070, - 0x070, 0x070, 0x06f, 0x06f, 0x06f, 0x06f, 0x06f, 0x06e, 0x06e, 0x06e, 0x06e, 0x06e, - 0x06d, 0x06d, 0x06d, 0x06d, 0x06d, 0x06c, 0x06c, 0x06c, 0x06c, 0x06c, 0x06b, 0x06b, - 0x06b, 0x06b, 0x06b, 0x06a, 0x06a, 0x06a, 0x06a, 0x06a, 0x069, 0x069, 0x069, 0x069, - 0x069, 0x069, 0x068, 0x068, 0x068, 0x068, 0x068, 0x067, 0x067, 0x067, 0x067, 0x067, - 0x066, 0x066, 0x066, 0x066, 0x066, 0x066, 0x065, 0x065, 0x065, 0x065, 0x065, 0x064, - 0x064, 0x064, 0x064, 0x064, 0x064, 0x063, 0x063, 0x063, 0x063, 0x063, 0x062, 0x062, - 0x062, 0x062, 0x062, 0x062, 0x061, 0x061, 0x061, 0x061, 0x061, 0x061, 0x060, 0x060, - 0x060, 0x060, 0x060, 0x060, 0x05f, 0x05f, 0x05f, 0x05f, 0x05f, 0x05e, 0x05e, 0x05e, - 0x05e, 0x05e, 0x05e, 0x05d, 0x05d, 0x05d, 0x05d, 0x05d, 0x05d, 0x05c, 0x05c, 0x05c, - 0x05c, 0x05c, 0x05c, 0x05b, 0x05b, 0x05b, 0x05b, 0x05b, 0x05b, 0x05a, 0x05a, 0x05a, - 0x05a, 0x05a, 0x05a, 0x05a, 0x059, 0x059, 0x059, 0x059, 0x059, 0x059, 0x058, 0x058, - 0x058, 0x058, 0x058, 0x058, 0x057, 0x057, 0x057, 0x057, 0x057, 0x057, 0x056, 0x056, - 0x056, 0x056, 0x056, 0x056, 0x056, 0x055, 0x055, 0x055, 0x055, 0x055, 0x055, 0x054, - 0x054, 0x054, 0x054, 0x054, 0x054, 0x054, 0x053, 0x053, 0x053, 0x053, 0x053, 0x053, - 0x053, 0x052, 0x052, 0x052, 0x052, 0x052, 0x052, 0x051, 0x051, 0x051, 0x051, 0x051, - 0x051, 0x051, 0x050, 0x050, 0x050, 0x050, 0x050, 0x050, 0x050, 0x04f, 0x04f, 0x04f, - 0x04f, 0x04f, 0x04f, 0x04f, 0x04e, 0x04e, 0x04e, 0x04e, 0x04e, 0x04e, 0x04e, 0x04d, - 0x04d, 0x04d, 0x04d, 0x04d, 0x04d, 0x04d, 0x04c, 0x04c, 0x04c, 0x04c, 0x04c, 0x04c, - 0x04c, 0x04c, 0x04b, 0x04b, 0x04b, 0x04b, 0x04b, 0x04b, 0x04b, 0x04a, 0x04a, 0x04a, - 0x04a, 0x04a, 0x04a, 0x04a, 0x04a, 0x049, 0x049, 0x049, 0x049, 0x049, 0x049, 0x049, - 0x048, 0x048, 0x048, 0x048, 0x048, 0x048, 0x048, 0x048, 0x047, 0x047, 0x047, 0x047, - 0x047, 0x047, 0x047, 0x047, 0x046, 0x046, 0x046, 0x046, 0x046, 0x046, 0x046, 0x046, - 0x045, 0x045, 0x045, 0x045, 0x045, 0x045, 0x045, 0x045, 0x044, 0x044, 0x044, 0x044, - 0x044, 0x044, 0x044, 0x044, 0x043, 0x043, 0x043, 0x043, 0x043, 0x043, 0x043, 0x043, - 0x042, 0x042, 0x042, 0x042, 0x042, 0x042, 0x042, 0x042, 0x041, 0x041, 0x041, 0x041, - 0x041, 0x041, 0x041, 0x041, 0x041, 0x040, 0x040, 0x040, 0x040, 0x040, 0x040, 0x040, - 0x040, 0x040, 0x03f, 0x03f, 0x03f, 0x03f, 0x03f, 0x03f, 0x03f, 0x03f, 0x03e, 0x03e, - 0x03e, 0x03e, 0x03e, 0x03e, 0x03e, 0x03e, 0x03e, 0x03d, 0x03d, 0x03d, 0x03d, 0x03d, - 0x03d, 0x03d, 0x03d, 0x03d, 0x03c, 0x03c, 0x03c, 0x03c, 0x03c, 0x03c, 0x03c, 0x03c, - 0x03c, 0x03c, 0x03b, 0x03b, 0x03b, 0x03b, 0x03b, 0x03b, 0x03b, 0x03b, 0x03b, 0x03a, - 0x03a, 0x03a, 0x03a, 0x03a, 0x03a, 0x03a, 0x03a, 0x03a, 0x03a, 0x039, 0x039, 0x039, - 0x039, 0x039, 0x039, 0x039, 0x039, 0x039, 0x038, 0x038, 0x038, 0x038, 0x038, 0x038, - 0x038, 0x038, 0x038, 0x038, 0x037, 0x037, 0x037, 0x037, 0x037, 0x037, 0x037, 0x037, - 0x037, 0x037, 0x036, 0x036, 0x036, 0x036, 0x036, 0x036, 0x036, 0x036, 0x036, 0x036, - 0x036, 0x035, 0x035, 0x035, 0x035, 0x035, 0x035, 0x035, 0x035, 0x035, 0x035, 0x034, - 0x034, 0x034, 0x034, 0x034, 0x034, 0x034, 0x034, 0x034, 0x034, 0x034, 0x033, 0x033, - 0x033, 0x033, 0x033, 0x033, 0x033, 0x033, 0x033, 0x033, 0x033, 0x032, 0x032, 0x032, - 0x032, 0x032, 0x032, 0x032, 0x032, 0x032, 0x032, 0x032, 0x031, 0x031, 0x031, 0x031, - 0x031, 0x031, 0x031, 0x031, 0x031, 0x031, 0x031, 0x030, 0x030, 0x030, 0x030, 0x030, - 0x030, 0x030, 0x030, 0x030, 0x030, 0x030, 0x030, 0x02f, 0x02f, 0x02f, 0x02f, 0x02f, - 0x02f, 0x02f, 0x02f, 0x02f, 0x02f, 0x02f, 0x02e, 0x02e, 0x02e, 0x02e, 0x02e, 0x02e, - 0x02e, 0x02e, 0x02e, 0x02e, 0x02e, 0x02e, 0x02d, 0x02d, 0x02d, 0x02d, 0x02d, 0x02d, - 0x02d, 0x02d, 0x02d, 0x02d, 0x02d, 0x02d, 0x02d, 0x02c, 0x02c, 0x02c, 0x02c, 0x02c, - 0x02c, 0x02c, 0x02c, 0x02c, 0x02c, 0x02c, 0x02c, 0x02b, 0x02b, 0x02b, 0x02b, 0x02b, - 0x02b, 0x02b, 0x02b, 0x02b, 0x02b, 0x02b, 0x02b, 0x02b, 0x02a, 0x02a, 0x02a, 0x02a, - 0x02a, 0x02a, 0x02a, 0x02a, 0x02a, 0x02a, 0x02a, 0x02a, 0x02a, 0x029, 0x029, 0x029, - 0x029, 0x029, 0x029, 0x029, 0x029, 0x029, 0x029, 0x029, 0x029, 0x029, 0x029, 0x028, - 0x028, 0x028, 0x028, 0x028, 0x028, 0x028, 0x028, 0x028, 0x028, 0x028, 0x028, 0x028, - 0x028, 0x027, 0x027, 0x027, 0x027, 0x027, 0x027, 0x027, 0x027, 0x027, 0x027, 0x027, - 0x027, 0x027, 0x027, 0x026, 0x026, 0x026, 0x026, 0x026, 0x026, 0x026, 0x026, 0x026, - 0x026, 0x026, 0x026, 0x026, 0x026, 0x026, 0x025, 0x025, 0x025, 0x025, 0x025, 0x025, - 0x025, 0x025, 0x025, 0x025, 0x025, 0x025, 0x025, 0x025, 0x025, 0x024, 0x024, 0x024, - 0x024, 0x024, 0x024, 0x024, 0x024, 0x024, 0x024, 0x024, 0x024, 0x024, 0x024, 0x024, - 0x023, 0x023, 0x023, 0x023, 0x023, 0x023, 0x023, 0x023, 0x023, 0x023, 0x023, 0x023, - 0x023, 0x023, 0x023, 0x023, 0x022, 0x022, 0x022, 0x022, 0x022, 0x022, 0x022, 0x022, - 0x022, 0x022, 0x022, 0x022, 0x022, 0x022, 0x022, 0x022, 0x021, 0x021, 0x021, 0x021, - 0x021, 0x021, 0x021, 0x021, 0x021, 0x021, 0x021, 0x021, 0x021, 0x021, 0x021, 0x021, - 0x021, 0x020, 0x020, 0x020, 0x020, 0x020, 0x020, 0x020, 0x020, 0x020, 0x020, 0x020, - 0x020, 0x020, 0x020, 0x020, 0x020, 0x020, 0x01f, 0x01f, 0x01f, 0x01f, 0x01f, 0x01f, - 0x01f, 0x01f, 0x01f, 0x01f, 0x01f, 0x01f, 0x01f, 0x01f, 0x01f, 0x01f, 0x01f, 0x01f, - 0x01e, 0x01e, 0x01e, 0x01e, 0x01e, 0x01e, 0x01e, 0x01e, 0x01e, 0x01e, 0x01e, 0x01e, - 0x01e, 0x01e, 0x01e, 0x01e, 0x01e, 0x01e, 0x01e, 0x01d, 0x01d, 0x01d, 0x01d, 0x01d, - 0x01d, 0x01d, 0x01d, 0x01d, 0x01d, 0x01d, 0x01d, 0x01d, 0x01d, 0x01d, 0x01d, 0x01d, - 0x01d, 0x01d, 0x01c, 0x01c, 0x01c, 0x01c, 0x01c, 0x01c, 0x01c, 0x01c, 0x01c, 0x01c, - 0x01c, 0x01c, 0x01c, 0x01c, 0x01c, 0x01c, 0x01c, 0x01c, 0x01c, 0x01b, 0x01b, 0x01b, - 0x01b, 0x01b, 0x01b, 0x01b, 0x01b, 0x01b, 0x01b, 0x01b, 0x01b, 0x01b, 0x01b, 0x01b, - 0x01b, 0x01b, 0x01b, 0x01b, 0x01b, 0x01b, 0x01a, 0x01a, 0x01a, 0x01a, 0x01a, 0x01a, - 0x01a, 0x01a, 0x01a, 0x01a, 0x01a, 0x01a, 0x01a, 0x01a, 0x01a, 0x01a, 0x01a, 0x01a, - 0x01a, 0x01a, 0x01a, 0x019, 0x019, 0x019, 0x019, 0x019, 0x019, 0x019, 0x019, 0x019, - 0x019, 0x019, 0x019, 0x019, 0x019, 0x019, 0x019, 0x019, 0x019, 0x019, 0x019, 0x019, - 0x019, 0x018, 0x018, 0x018, 0x018, 0x018, 0x018, 0x018, 0x018, 0x018, 0x018, 0x018, - 0x018, 0x018, 0x018, 0x018, 0x018, 0x018, 0x018, 0x018, 0x018, 0x018, 0x018, 0x018, - 0x018, 0x017, 0x017, 0x017, 0x017, 0x017, 0x017, 0x017, 0x017, 0x017, 0x017, 0x017, - 0x017, 0x017, 0x017, 0x017, 0x017, 0x017, 0x017, 0x017, 0x017, 0x017, 0x017, 0x017, - 0x017, 0x016, 0x016, 0x016, 0x016, 0x016, 0x016, 0x016, 0x016, 0x016, 0x016, 0x016, - 0x016, 0x016, 0x016, 0x016, 0x016, 0x016, 0x016, 0x016, 0x016, 0x016, 0x016, 0x016, - 0x016, 0x016, 0x015, 0x015, 0x015, 0x015, 0x015, 0x015, 0x015, 0x015, 0x015, 0x015, - 0x015, 0x015, 0x015, 0x015, 0x015, 0x015, 0x015, 0x015, 0x015, 0x015, 0x015, 0x015, - 0x015, 0x015, 0x015, 0x015, 0x014, 0x014, 0x014, 0x014, 0x014, 0x014, 0x014, 0x014, - 0x014, 0x014, 0x014, 0x014, 0x014, 0x014, 0x014, 0x014, 0x014, 0x014, 0x014, 0x014, - 0x014, 0x014, 0x014, 0x014, 0x014, 0x014, 0x014, 0x014, 0x013, 0x013, 0x013, 0x013, - 0x013, 0x013, 0x013, 0x013, 0x013, 0x013, 0x013, 0x013, 0x013, 0x013, 0x013, 0x013, - 0x013, 0x013, 0x013, 0x013, 0x013, 0x013, 0x013, 0x013, 0x013, 0x013, 0x013, 0x013, - 0x013, 0x012, 0x012, 0x012, 0x012, 0x012, 0x012, 0x012, 0x012, 0x012, 0x012, 0x012, - 0x012, 0x012, 0x012, 0x012, 0x012, 0x012, 0x012, 0x012, 0x012, 0x012, 0x012, 0x012, - 0x012, 0x012, 0x012, 0x012, 0x012, 0x012, 0x012, 0x012, 0x011, 0x011, 0x011, 0x011, - 0x011, 0x011, 0x011, 0x011, 0x011, 0x011, 0x011, 0x011, 0x011, 0x011, 0x011, 0x011, - 0x011, 0x011, 0x011, 0x011, 0x011, 0x011, 0x011, 0x011, 0x011, 0x011, 0x011, 0x011, - 0x011, 0x011, 0x011, 0x011, 0x011, 0x010, 0x010, 0x010, 0x010, 0x010, 0x010, 0x010, - 0x010, 0x010, 0x010, 0x010, 0x010, 0x010, 0x010, 0x010, 0x010, 0x010, 0x010, 0x010, - 0x010, 0x010, 0x010, 0x010, 0x010, 0x010, 0x010, 0x010, 0x010, 0x010, 0x010, 0x010, - 0x010, 0x010, 0x010, 0x00f, 0x00f, 0x00f, 0x00f, 0x00f, 0x00f, 0x00f, 0x00f, 0x00f, - 0x00f, 0x00f, 0x00f, 0x00f, 0x00f, 0x00f, 0x00f, 0x00f, 0x00f, 0x00f, 0x00f, 0x00f, - 0x00f, 0x00f, 0x00f, 0x00f, 0x00f, 0x00f, 0x00f, 0x00f, 0x00f, 0x00f, 0x00f, 0x00f, - 0x00f, 0x00f, 0x00f, 0x00f, 0x00e, 0x00e, 0x00e, 0x00e, 0x00e, 0x00e, 0x00e, 0x00e, - 0x00e, 0x00e, 0x00e, 0x00e, 0x00e, 0x00e, 0x00e, 0x00e, 0x00e, 0x00e, 0x00e, 0x00e, - 0x00e, 0x00e, 0x00e, 0x00e, 0x00e, 0x00e, 0x00e, 0x00e, 0x00e, 0x00e, 0x00e, 0x00e, - 0x00e, 0x00e, 0x00e, 0x00e, 0x00e, 0x00e, 0x00e, 0x00e, 0x00d, 0x00d, 0x00d, 0x00d, - 0x00d, 0x00d, 0x00d, 0x00d, 0x00d, 0x00d, 0x00d, 0x00d, 0x00d, 0x00d, 0x00d, 0x00d, - 0x00d, 0x00d, 0x00d, 0x00d, 0x00d, 0x00d, 0x00d, 0x00d, 0x00d, 0x00d, 0x00d, 0x00d, - 0x00d, 0x00d, 0x00d, 0x00d, 0x00d, 0x00d, 0x00d, 0x00d, 0x00d, 0x00d, 0x00d, 0x00d, - 0x00d, 0x00d, 0x00d, 0x00c, 0x00c, 0x00c, 0x00c, 0x00c, 0x00c, 0x00c, 0x00c, 0x00c, - 0x00c, 0x00c, 0x00c, 0x00c, 0x00c, 0x00c, 0x00c, 0x00c, 0x00c, 0x00c, 0x00c, 0x00c, - 0x00c, 0x00c, 0x00c, 0x00c, 0x00c, 0x00c, 0x00c, 0x00c, 0x00c, 0x00c, 0x00c, 0x00c, - 0x00c, 0x00c, 0x00c, 0x00c, 0x00c, 0x00c, 0x00c, 0x00c, 0x00c, 0x00c, 0x00c, 0x00c, - 0x00c, 0x00b, 0x00b, 0x00b, 0x00b, 0x00b, 0x00b, 0x00b, 0x00b, 0x00b, 0x00b, 0x00b, - 0x00b, 0x00b, 0x00b, 0x00b, 0x00b, 0x00b, 0x00b, 0x00b, 0x00b, 0x00b, 0x00b, 0x00b, - 0x00b, 0x00b, 0x00b, 0x00b, 0x00b, 0x00b, 0x00b, 0x00b, 0x00b, 0x00b, 0x00b, 0x00b, - 0x00b, 0x00b, 0x00b, 0x00b, 0x00b, 0x00b, 0x00b, 0x00b, 0x00b, 0x00b, 0x00b, 0x00b, - 0x00b, 0x00b, 0x00b, 0x00b, 0x00a, 0x00a, 0x00a, 0x00a, 0x00a, 0x00a, 0x00a, 0x00a, - 0x00a, 0x00a, 0x00a, 0x00a, 0x00a, 0x00a, 0x00a, 0x00a, 0x00a, 0x00a, 0x00a, 0x00a, - 0x00a, 0x00a, 0x00a, 0x00a, 0x00a, 0x00a, 0x00a, 0x00a, 0x00a, 0x00a, 0x00a, 0x00a, - 0x00a, 0x00a, 0x00a, 0x00a, 0x00a, 0x00a, 0x00a, 0x00a, 0x00a, 0x00a, 0x00a, 0x00a, - 0x00a, 0x00a, 0x00a, 0x00a, 0x00a, 0x00a, 0x00a, 0x00a, 0x00a, 0x00a, 0x00a, 0x00a, - 0x009, 0x009, 0x009, 0x009, 0x009, 0x009, 0x009, 0x009, 0x009, 0x009, 0x009, 0x009, - 0x009, 0x009, 0x009, 0x009, 0x009, 0x009, 0x009, 0x009, 0x009, 0x009, 0x009, 0x009, - 0x009, 0x009, 0x009, 0x009, 0x009, 0x009, 0x009, 0x009, 0x009, 0x009, 0x009, 0x009, - 0x009, 0x009, 0x009, 0x009, 0x009, 0x009, 0x009, 0x009, 0x009, 0x009, 0x009, 0x009, - 0x009, 0x009, 0x009, 0x009, 0x009, 0x009, 0x009, 0x009, 0x009, 0x009, 0x009, 0x009, - 0x009, 0x009, 0x008, 0x008, 0x008, 0x008, 0x008, 0x008, 0x008, 0x008, 0x008, 0x008, - 0x008, 0x008, 0x008, 0x008, 0x008, 0x008, 0x008, 0x008, 0x008, 0x008, 0x008, 0x008, - 0x008, 0x008, 0x008, 0x008, 0x008, 0x008, 0x008, 0x008, 0x008, 0x008, 0x008, 0x008, - 0x008, 0x008, 0x008, 0x008, 0x008, 0x008, 0x008, 0x008, 0x008, 0x008, 0x008, 0x008, - 0x008, 0x008, 0x008, 0x008, 0x008, 0x008, 0x008, 0x008, 0x008, 0x008, 0x008, 0x008, - 0x008, 0x008, 0x008, 0x008, 0x008, 0x008, 0x008, 0x008, 0x008, 0x008, 0x008, 0x008, - 0x007, 0x007, 0x007, 0x007, 0x007, 0x007, 0x007, 0x007, 0x007, 0x007, 0x007, 0x007, - 0x007, 0x007, 0x007, 0x007, 0x007, 0x007, 0x007, 0x007, 0x007, 0x007, 0x007, 0x007, - 0x007, 0x007, 0x007, 0x007, 0x007, 0x007, 0x007, 0x007, 0x007, 0x007, 0x007, 0x007, - 0x007, 0x007, 0x007, 0x007, 0x007, 0x007, 0x007, 0x007, 0x007, 0x007, 0x007, 0x007, - 0x007, 0x007, 0x007, 0x007, 0x007, 0x007, 0x007, 0x007, 0x007, 0x007, 0x007, 0x007, - 0x007, 0x007, 0x007, 0x007, 0x007, 0x007, 0x007, 0x007, 0x007, 0x007, 0x007, 0x007, - 0x007, 0x007, 0x007, 0x007, 0x007, 0x007, 0x007, 0x007, 0x006, 0x006, 0x006, 0x006, - 0x006, 0x006, 0x006, 0x006, 0x006, 0x006, 0x006, 0x006, 0x006, 0x006, 0x006, 0x006, - 0x006, 0x006, 0x006, 0x006, 0x006, 0x006, 0x006, 0x006, 0x006, 0x006, 0x006, 0x006, - 0x006, 0x006, 0x006, 0x006, 0x006, 0x006, 0x006, 0x006, 0x006, 0x006, 0x006, 0x006, - 0x006, 0x006, 0x006, 0x006, 0x006, 0x006, 0x006, 0x006, 0x006, 0x006, 0x006, 0x006, - 0x006, 0x006, 0x006, 0x006, 0x006, 0x006, 0x006, 0x006, 0x006, 0x006, 0x006, 0x006, - 0x006, 0x006, 0x006, 0x006, 0x006, 0x006, 0x006, 0x006, 0x006, 0x006, 0x006, 0x006, - 0x006, 0x006, 0x006, 0x006, 0x006, 0x006, 0x006, 0x006, 0x006, 0x006, 0x006, 0x006, - 0x006, 0x006, 0x006, 0x006, 0x006, 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, - 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, - 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, - 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, - 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, - 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, - 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, - 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, - 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, - 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, 0x004, - 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, - 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, - 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, - 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, - 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, - 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, - 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, - 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, - 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, - 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, - 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, - 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x003, - 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, - 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, - 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, - 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, - 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, - 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, - 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, - 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, - 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, - 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, - 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, - 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, - 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, - 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, - 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, - 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, - 0x003, 0x003, 0x003, 0x003, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, - 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, - 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, - 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, - 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, - 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, - 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, - 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, - 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, - 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, - 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, - 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, - 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, - 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, - 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, - 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, - 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, - 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, - 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, - 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, - 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, - 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, - 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, - 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, - 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, - 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, - 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, - 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, - 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, - 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, - 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, - 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, - 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, - 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, - 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, - 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, - 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, - 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, - 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, - 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, - 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, - 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, - 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, - 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, - 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, - 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, - 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, - 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, - 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, - 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, - 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, - 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, - 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, - 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, - 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, - 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, - 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, - 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, - 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, - 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, - 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, - 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, - 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, - 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, - 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, - 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, - 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, - 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, - 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, - 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, - 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, - 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, - 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, - 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, - 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, - 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001 -}; - -const uint16_t centTableSSGSaw[3072] = { - 0x0ef, 0x0ee, 0x0ee, 0x0ed, 0x0ed, 0x0ec, 0x0ec, 0x0ec, 0x0eb, 0x0eb, 0x0ea, 0x0ea, - 0x0e9, 0x0e9, 0x0e9, 0x0e8, 0x0e8, 0x0e7, 0x0e7, 0x0e6, 0x0e6, 0x0e6, 0x0e5, 0x0e5, - 0x0e4, 0x0e4, 0x0e4, 0x0e3, 0x0e3, 0x0e2, 0x0e2, 0x0e2, 0x0e1, 0x0e1, 0x0e0, 0x0e0, - 0x0e0, 0x0df, 0x0df, 0x0de, 0x0de, 0x0dd, 0x0dd, 0x0dd, 0x0dc, 0x0dc, 0x0dc, 0x0db, - 0x0db, 0x0da, 0x0da, 0x0da, 0x0d9, 0x0d9, 0x0d8, 0x0d8, 0x0d8, 0x0d7, 0x0d7, 0x0d6, - 0x0d6, 0x0d6, 0x0d5, 0x0d5, 0x0d4, 0x0d4, 0x0d4, 0x0d3, 0x0d3, 0x0d3, 0x0d2, 0x0d2, - 0x0d1, 0x0d1, 0x0d1, 0x0d0, 0x0d0, 0x0d0, 0x0cf, 0x0cf, 0x0ce, 0x0ce, 0x0ce, 0x0cd, - 0x0cd, 0x0cd, 0x0cc, 0x0cc, 0x0cb, 0x0cb, 0x0cb, 0x0ca, 0x0ca, 0x0ca, 0x0c9, 0x0c9, - 0x0c9, 0x0c8, 0x0c8, 0x0c7, 0x0c7, 0x0c7, 0x0c6, 0x0c6, 0x0c6, 0x0c5, 0x0c5, 0x0c5, - 0x0c4, 0x0c4, 0x0c4, 0x0c3, 0x0c3, 0x0c3, 0x0c2, 0x0c2, 0x0c1, 0x0c1, 0x0c1, 0x0c0, - 0x0c0, 0x0c0, 0x0bf, 0x0bf, 0x0bf, 0x0be, 0x0be, 0x0be, 0x0bd, 0x0bd, 0x0bd, 0x0bc, - 0x0bc, 0x0bc, 0x0bb, 0x0bb, 0x0bb, 0x0ba, 0x0ba, 0x0ba, 0x0b9, 0x0b9, 0x0b9, 0x0b8, - 0x0b8, 0x0b8, 0x0b7, 0x0b7, 0x0b7, 0x0b6, 0x0b6, 0x0b6, 0x0b5, 0x0b5, 0x0b5, 0x0b4, - 0x0b4, 0x0b4, 0x0b3, 0x0b3, 0x0b3, 0x0b2, 0x0b2, 0x0b2, 0x0b1, 0x0b1, 0x0b1, 0x0b0, - 0x0b0, 0x0b0, 0x0af, 0x0af, 0x0af, 0x0af, 0x0ae, 0x0ae, 0x0ae, 0x0ad, 0x0ad, 0x0ad, - 0x0ac, 0x0ac, 0x0ac, 0x0ab, 0x0ab, 0x0ab, 0x0aa, 0x0aa, 0x0aa, 0x0aa, 0x0a9, 0x0a9, - 0x0a9, 0x0a8, 0x0a8, 0x0a8, 0x0a7, 0x0a7, 0x0a7, 0x0a7, 0x0a6, 0x0a6, 0x0a6, 0x0a5, - 0x0a5, 0x0a5, 0x0a4, 0x0a4, 0x0a4, 0x0a4, 0x0a3, 0x0a3, 0x0a3, 0x0a2, 0x0a2, 0x0a2, - 0x0a2, 0x0a1, 0x0a1, 0x0a1, 0x0a0, 0x0a0, 0x0a0, 0x09f, 0x09f, 0x09f, 0x09f, 0x09e, - 0x09e, 0x09e, 0x09d, 0x09d, 0x09d, 0x09d, 0x09c, 0x09c, 0x09c, 0x09b, 0x09b, 0x09b, - 0x09b, 0x09a, 0x09a, 0x09a, 0x09a, 0x099, 0x099, 0x099, 0x098, 0x098, 0x098, 0x098, - 0x097, 0x097, 0x097, 0x097, 0x096, 0x096, 0x096, 0x095, 0x095, 0x095, 0x095, 0x094, - 0x094, 0x094, 0x094, 0x093, 0x093, 0x093, 0x093, 0x092, 0x092, 0x092, 0x091, 0x091, - 0x091, 0x091, 0x090, 0x090, 0x090, 0x090, 0x08f, 0x08f, 0x08f, 0x08f, 0x08e, 0x08e, - 0x08e, 0x08e, 0x08d, 0x08d, 0x08d, 0x08d, 0x08c, 0x08c, 0x08c, 0x08c, 0x08b, 0x08b, - 0x08b, 0x08b, 0x08a, 0x08a, 0x08a, 0x08a, 0x089, 0x089, 0x089, 0x089, 0x088, 0x088, - 0x088, 0x088, 0x087, 0x087, 0x087, 0x087, 0x086, 0x086, 0x086, 0x086, 0x085, 0x085, - 0x085, 0x085, 0x084, 0x084, 0x084, 0x084, 0x083, 0x083, 0x083, 0x083, 0x083, 0x082, - 0x082, 0x082, 0x082, 0x081, 0x081, 0x081, 0x081, 0x080, 0x080, 0x080, 0x080, 0x07f, - 0x07f, 0x07f, 0x07f, 0x07f, 0x07e, 0x07e, 0x07e, 0x07e, 0x07d, 0x07d, 0x07d, 0x07d, - 0x07d, 0x07c, 0x07c, 0x07c, 0x07c, 0x07b, 0x07b, 0x07b, 0x07b, 0x07b, 0x07a, 0x07a, - 0x07a, 0x07a, 0x079, 0x079, 0x079, 0x079, 0x079, 0x078, 0x078, 0x078, 0x078, 0x077, - 0x077, 0x077, 0x077, 0x077, 0x076, 0x076, 0x076, 0x076, 0x076, 0x075, 0x075, 0x075, - 0x075, 0x074, 0x074, 0x074, 0x074, 0x074, 0x073, 0x073, 0x073, 0x073, 0x073, 0x072, - 0x072, 0x072, 0x072, 0x072, 0x071, 0x071, 0x071, 0x071, 0x071, 0x070, 0x070, 0x070, - 0x070, 0x070, 0x06f, 0x06f, 0x06f, 0x06f, 0x06f, 0x06e, 0x06e, 0x06e, 0x06e, 0x06e, - 0x06d, 0x06d, 0x06d, 0x06d, 0x06d, 0x06c, 0x06c, 0x06c, 0x06c, 0x06c, 0x06b, 0x06b, - 0x06b, 0x06b, 0x06b, 0x06a, 0x06a, 0x06a, 0x06a, 0x06a, 0x069, 0x069, 0x069, 0x069, - 0x069, 0x069, 0x068, 0x068, 0x068, 0x068, 0x068, 0x067, 0x067, 0x067, 0x067, 0x067, - 0x066, 0x066, 0x066, 0x066, 0x066, 0x066, 0x065, 0x065, 0x065, 0x065, 0x065, 0x064, - 0x064, 0x064, 0x064, 0x064, 0x064, 0x063, 0x063, 0x063, 0x063, 0x063, 0x062, 0x062, - 0x062, 0x062, 0x062, 0x062, 0x061, 0x061, 0x061, 0x061, 0x061, 0x061, 0x060, 0x060, - 0x060, 0x060, 0x060, 0x060, 0x05f, 0x05f, 0x05f, 0x05f, 0x05f, 0x05e, 0x05e, 0x05e, - 0x05e, 0x05e, 0x05e, 0x05d, 0x05d, 0x05d, 0x05d, 0x05d, 0x05d, 0x05c, 0x05c, 0x05c, - 0x05c, 0x05c, 0x05c, 0x05b, 0x05b, 0x05b, 0x05b, 0x05b, 0x05b, 0x05a, 0x05a, 0x05a, - 0x05a, 0x05a, 0x05a, 0x05a, 0x059, 0x059, 0x059, 0x059, 0x059, 0x059, 0x058, 0x058, - 0x058, 0x058, 0x058, 0x058, 0x057, 0x057, 0x057, 0x057, 0x057, 0x057, 0x056, 0x056, - 0x056, 0x056, 0x056, 0x056, 0x056, 0x055, 0x055, 0x055, 0x055, 0x055, 0x055, 0x054, - 0x054, 0x054, 0x054, 0x054, 0x054, 0x054, 0x053, 0x053, 0x053, 0x053, 0x053, 0x053, - 0x053, 0x052, 0x052, 0x052, 0x052, 0x052, 0x052, 0x051, 0x051, 0x051, 0x051, 0x051, - 0x051, 0x051, 0x050, 0x050, 0x050, 0x050, 0x050, 0x050, 0x050, 0x04f, 0x04f, 0x04f, - 0x04f, 0x04f, 0x04f, 0x04f, 0x04e, 0x04e, 0x04e, 0x04e, 0x04e, 0x04e, 0x04e, 0x04d, - 0x04d, 0x04d, 0x04d, 0x04d, 0x04d, 0x04d, 0x04c, 0x04c, 0x04c, 0x04c, 0x04c, 0x04c, - 0x04c, 0x04c, 0x04b, 0x04b, 0x04b, 0x04b, 0x04b, 0x04b, 0x04b, 0x04a, 0x04a, 0x04a, - 0x04a, 0x04a, 0x04a, 0x04a, 0x04a, 0x049, 0x049, 0x049, 0x049, 0x049, 0x049, 0x049, - 0x048, 0x048, 0x048, 0x048, 0x048, 0x048, 0x048, 0x048, 0x047, 0x047, 0x047, 0x047, - 0x047, 0x047, 0x047, 0x047, 0x046, 0x046, 0x046, 0x046, 0x046, 0x046, 0x046, 0x046, - 0x045, 0x045, 0x045, 0x045, 0x045, 0x045, 0x045, 0x045, 0x044, 0x044, 0x044, 0x044, - 0x044, 0x044, 0x044, 0x044, 0x043, 0x043, 0x043, 0x043, 0x043, 0x043, 0x043, 0x043, - 0x042, 0x042, 0x042, 0x042, 0x042, 0x042, 0x042, 0x042, 0x041, 0x041, 0x041, 0x041, - 0x041, 0x041, 0x041, 0x041, 0x041, 0x040, 0x040, 0x040, 0x040, 0x040, 0x040, 0x040, - 0x040, 0x040, 0x03f, 0x03f, 0x03f, 0x03f, 0x03f, 0x03f, 0x03f, 0x03f, 0x03e, 0x03e, - 0x03e, 0x03e, 0x03e, 0x03e, 0x03e, 0x03e, 0x03e, 0x03d, 0x03d, 0x03d, 0x03d, 0x03d, - 0x03d, 0x03d, 0x03d, 0x03d, 0x03c, 0x03c, 0x03c, 0x03c, 0x03c, 0x03c, 0x03c, 0x03c, - 0x03c, 0x03c, 0x03b, 0x03b, 0x03b, 0x03b, 0x03b, 0x03b, 0x03b, 0x03b, 0x03b, 0x03a, - 0x03a, 0x03a, 0x03a, 0x03a, 0x03a, 0x03a, 0x03a, 0x03a, 0x03a, 0x039, 0x039, 0x039, - 0x039, 0x039, 0x039, 0x039, 0x039, 0x039, 0x038, 0x038, 0x038, 0x038, 0x038, 0x038, - 0x038, 0x038, 0x038, 0x038, 0x037, 0x037, 0x037, 0x037, 0x037, 0x037, 0x037, 0x037, - 0x037, 0x037, 0x036, 0x036, 0x036, 0x036, 0x036, 0x036, 0x036, 0x036, 0x036, 0x036, - 0x036, 0x035, 0x035, 0x035, 0x035, 0x035, 0x035, 0x035, 0x035, 0x035, 0x035, 0x034, - 0x034, 0x034, 0x034, 0x034, 0x034, 0x034, 0x034, 0x034, 0x034, 0x034, 0x033, 0x033, - 0x033, 0x033, 0x033, 0x033, 0x033, 0x033, 0x033, 0x033, 0x033, 0x032, 0x032, 0x032, - 0x032, 0x032, 0x032, 0x032, 0x032, 0x032, 0x032, 0x032, 0x031, 0x031, 0x031, 0x031, - 0x031, 0x031, 0x031, 0x031, 0x031, 0x031, 0x031, 0x030, 0x030, 0x030, 0x030, 0x030, - 0x030, 0x030, 0x030, 0x030, 0x030, 0x030, 0x030, 0x02f, 0x02f, 0x02f, 0x02f, 0x02f, - 0x02f, 0x02f, 0x02f, 0x02f, 0x02f, 0x02f, 0x02e, 0x02e, 0x02e, 0x02e, 0x02e, 0x02e, - 0x02e, 0x02e, 0x02e, 0x02e, 0x02e, 0x02e, 0x02d, 0x02d, 0x02d, 0x02d, 0x02d, 0x02d, - 0x02d, 0x02d, 0x02d, 0x02d, 0x02d, 0x02d, 0x02d, 0x02c, 0x02c, 0x02c, 0x02c, 0x02c, - 0x02c, 0x02c, 0x02c, 0x02c, 0x02c, 0x02c, 0x02c, 0x02b, 0x02b, 0x02b, 0x02b, 0x02b, - 0x02b, 0x02b, 0x02b, 0x02b, 0x02b, 0x02b, 0x02b, 0x02b, 0x02a, 0x02a, 0x02a, 0x02a, - 0x02a, 0x02a, 0x02a, 0x02a, 0x02a, 0x02a, 0x02a, 0x02a, 0x02a, 0x029, 0x029, 0x029, - 0x029, 0x029, 0x029, 0x029, 0x029, 0x029, 0x029, 0x029, 0x029, 0x029, 0x029, 0x028, - 0x028, 0x028, 0x028, 0x028, 0x028, 0x028, 0x028, 0x028, 0x028, 0x028, 0x028, 0x028, - 0x028, 0x027, 0x027, 0x027, 0x027, 0x027, 0x027, 0x027, 0x027, 0x027, 0x027, 0x027, - 0x027, 0x027, 0x027, 0x026, 0x026, 0x026, 0x026, 0x026, 0x026, 0x026, 0x026, 0x026, - 0x026, 0x026, 0x026, 0x026, 0x026, 0x026, 0x025, 0x025, 0x025, 0x025, 0x025, 0x025, - 0x025, 0x025, 0x025, 0x025, 0x025, 0x025, 0x025, 0x025, 0x025, 0x024, 0x024, 0x024, - 0x024, 0x024, 0x024, 0x024, 0x024, 0x024, 0x024, 0x024, 0x024, 0x024, 0x024, 0x024, - 0x023, 0x023, 0x023, 0x023, 0x023, 0x023, 0x023, 0x023, 0x023, 0x023, 0x023, 0x023, - 0x023, 0x023, 0x023, 0x023, 0x022, 0x022, 0x022, 0x022, 0x022, 0x022, 0x022, 0x022, - 0x022, 0x022, 0x022, 0x022, 0x022, 0x022, 0x022, 0x022, 0x021, 0x021, 0x021, 0x021, - 0x021, 0x021, 0x021, 0x021, 0x021, 0x021, 0x021, 0x021, 0x021, 0x021, 0x021, 0x021, - 0x021, 0x020, 0x020, 0x020, 0x020, 0x020, 0x020, 0x020, 0x020, 0x020, 0x020, 0x020, - 0x020, 0x020, 0x020, 0x020, 0x020, 0x020, 0x01f, 0x01f, 0x01f, 0x01f, 0x01f, 0x01f, - 0x01f, 0x01f, 0x01f, 0x01f, 0x01f, 0x01f, 0x01f, 0x01f, 0x01f, 0x01f, 0x01f, 0x01f, - 0x01e, 0x01e, 0x01e, 0x01e, 0x01e, 0x01e, 0x01e, 0x01e, 0x01e, 0x01e, 0x01e, 0x01e, - 0x01e, 0x01e, 0x01e, 0x01e, 0x01e, 0x01e, 0x01e, 0x01d, 0x01d, 0x01d, 0x01d, 0x01d, - 0x01d, 0x01d, 0x01d, 0x01d, 0x01d, 0x01d, 0x01d, 0x01d, 0x01d, 0x01d, 0x01d, 0x01d, - 0x01d, 0x01d, 0x01c, 0x01c, 0x01c, 0x01c, 0x01c, 0x01c, 0x01c, 0x01c, 0x01c, 0x01c, - 0x01c, 0x01c, 0x01c, 0x01c, 0x01c, 0x01c, 0x01c, 0x01c, 0x01c, 0x01b, 0x01b, 0x01b, - 0x01b, 0x01b, 0x01b, 0x01b, 0x01b, 0x01b, 0x01b, 0x01b, 0x01b, 0x01b, 0x01b, 0x01b, - 0x01b, 0x01b, 0x01b, 0x01b, 0x01b, 0x01b, 0x01a, 0x01a, 0x01a, 0x01a, 0x01a, 0x01a, - 0x01a, 0x01a, 0x01a, 0x01a, 0x01a, 0x01a, 0x01a, 0x01a, 0x01a, 0x01a, 0x01a, 0x01a, - 0x01a, 0x01a, 0x01a, 0x019, 0x019, 0x019, 0x019, 0x019, 0x019, 0x019, 0x019, 0x019, - 0x019, 0x019, 0x019, 0x019, 0x019, 0x019, 0x019, 0x019, 0x019, 0x019, 0x019, 0x019, - 0x019, 0x018, 0x018, 0x018, 0x018, 0x018, 0x018, 0x018, 0x018, 0x018, 0x018, 0x018, - 0x018, 0x018, 0x018, 0x018, 0x018, 0x018, 0x018, 0x018, 0x018, 0x018, 0x018, 0x018, - 0x018, 0x017, 0x017, 0x017, 0x017, 0x017, 0x017, 0x017, 0x017, 0x017, 0x017, 0x017, - 0x017, 0x017, 0x017, 0x017, 0x017, 0x017, 0x017, 0x017, 0x017, 0x017, 0x017, 0x017, - 0x017, 0x016, 0x016, 0x016, 0x016, 0x016, 0x016, 0x016, 0x016, 0x016, 0x016, 0x016, - 0x016, 0x016, 0x016, 0x016, 0x016, 0x016, 0x016, 0x016, 0x016, 0x016, 0x016, 0x016, - 0x016, 0x016, 0x015, 0x015, 0x015, 0x015, 0x015, 0x015, 0x015, 0x015, 0x015, 0x015, - 0x015, 0x015, 0x015, 0x015, 0x015, 0x015, 0x015, 0x015, 0x015, 0x015, 0x015, 0x015, - 0x015, 0x015, 0x015, 0x015, 0x014, 0x014, 0x014, 0x014, 0x014, 0x014, 0x014, 0x014, - 0x014, 0x014, 0x014, 0x014, 0x014, 0x014, 0x014, 0x014, 0x014, 0x014, 0x014, 0x014, - 0x014, 0x014, 0x014, 0x014, 0x014, 0x014, 0x014, 0x014, 0x013, 0x013, 0x013, 0x013, - 0x013, 0x013, 0x013, 0x013, 0x013, 0x013, 0x013, 0x013, 0x013, 0x013, 0x013, 0x013, - 0x013, 0x013, 0x013, 0x013, 0x013, 0x013, 0x013, 0x013, 0x013, 0x013, 0x013, 0x013, - 0x013, 0x012, 0x012, 0x012, 0x012, 0x012, 0x012, 0x012, 0x012, 0x012, 0x012, 0x012, - 0x012, 0x012, 0x012, 0x012, 0x012, 0x012, 0x012, 0x012, 0x012, 0x012, 0x012, 0x012, - 0x012, 0x012, 0x012, 0x012, 0x012, 0x012, 0x012, 0x012, 0x011, 0x011, 0x011, 0x011, - 0x011, 0x011, 0x011, 0x011, 0x011, 0x011, 0x011, 0x011, 0x011, 0x011, 0x011, 0x011, - 0x011, 0x011, 0x011, 0x011, 0x011, 0x011, 0x011, 0x011, 0x011, 0x011, 0x011, 0x011, - 0x011, 0x011, 0x011, 0x011, 0x011, 0x010, 0x010, 0x010, 0x010, 0x010, 0x010, 0x010, - 0x010, 0x010, 0x010, 0x010, 0x010, 0x010, 0x010, 0x010, 0x010, 0x010, 0x010, 0x010, - 0x010, 0x010, 0x010, 0x010, 0x010, 0x010, 0x010, 0x010, 0x010, 0x010, 0x010, 0x010, - 0x010, 0x010, 0x010, 0x00f, 0x00f, 0x00f, 0x00f, 0x00f, 0x00f, 0x00f, 0x00f, 0x00f, - 0x00f, 0x00f, 0x00f, 0x00f, 0x00f, 0x00f, 0x00f, 0x00f, 0x00f, 0x00f, 0x00f, 0x00f, - 0x00f, 0x00f, 0x00f, 0x00f, 0x00f, 0x00f, 0x00f, 0x00f, 0x00f, 0x00f, 0x00f, 0x00f, - 0x00f, 0x00f, 0x00f, 0x00f, 0x00e, 0x00e, 0x00e, 0x00e, 0x00e, 0x00e, 0x00e, 0x00e, - 0x00e, 0x00e, 0x00e, 0x00e, 0x00e, 0x00e, 0x00e, 0x00e, 0x00e, 0x00e, 0x00e, 0x00e, - 0x00e, 0x00e, 0x00e, 0x00e, 0x00e, 0x00e, 0x00e, 0x00e, 0x00e, 0x00e, 0x00e, 0x00e, - 0x00e, 0x00e, 0x00e, 0x00e, 0x00e, 0x00e, 0x00e, 0x00e, 0x00d, 0x00d, 0x00d, 0x00d, - 0x00d, 0x00d, 0x00d, 0x00d, 0x00d, 0x00d, 0x00d, 0x00d, 0x00d, 0x00d, 0x00d, 0x00d, - 0x00d, 0x00d, 0x00d, 0x00d, 0x00d, 0x00d, 0x00d, 0x00d, 0x00d, 0x00d, 0x00d, 0x00d, - 0x00d, 0x00d, 0x00d, 0x00d, 0x00d, 0x00d, 0x00d, 0x00d, 0x00d, 0x00d, 0x00d, 0x00d, - 0x00d, 0x00d, 0x00d, 0x00c, 0x00c, 0x00c, 0x00c, 0x00c, 0x00c, 0x00c, 0x00c, 0x00c, - 0x00c, 0x00c, 0x00c, 0x00c, 0x00c, 0x00c, 0x00c, 0x00c, 0x00c, 0x00c, 0x00c, 0x00c, - 0x00c, 0x00c, 0x00c, 0x00c, 0x00c, 0x00c, 0x00c, 0x00c, 0x00c, 0x00c, 0x00c, 0x00c, - 0x00c, 0x00c, 0x00c, 0x00c, 0x00c, 0x00c, 0x00c, 0x00c, 0x00c, 0x00c, 0x00c, 0x00c, - 0x00c, 0x00b, 0x00b, 0x00b, 0x00b, 0x00b, 0x00b, 0x00b, 0x00b, 0x00b, 0x00b, 0x00b, - 0x00b, 0x00b, 0x00b, 0x00b, 0x00b, 0x00b, 0x00b, 0x00b, 0x00b, 0x00b, 0x00b, 0x00b, - 0x00b, 0x00b, 0x00b, 0x00b, 0x00b, 0x00b, 0x00b, 0x00b, 0x00b, 0x00b, 0x00b, 0x00b, - 0x00b, 0x00b, 0x00b, 0x00b, 0x00b, 0x00b, 0x00b, 0x00b, 0x00b, 0x00b, 0x00b, 0x00b, - 0x00b, 0x00b, 0x00b, 0x00b, 0x00a, 0x00a, 0x00a, 0x00a, 0x00a, 0x00a, 0x00a, 0x00a, - 0x00a, 0x00a, 0x00a, 0x00a, 0x00a, 0x00a, 0x00a, 0x00a, 0x00a, 0x00a, 0x00a, 0x00a, - 0x00a, 0x00a, 0x00a, 0x00a, 0x00a, 0x00a, 0x00a, 0x00a, 0x00a, 0x00a, 0x00a, 0x00a, - 0x00a, 0x00a, 0x00a, 0x00a, 0x00a, 0x00a, 0x00a, 0x00a, 0x00a, 0x00a, 0x00a, 0x00a, - 0x00a, 0x00a, 0x00a, 0x00a, 0x00a, 0x00a, 0x00a, 0x00a, 0x00a, 0x00a, 0x00a, 0x00a, - 0x009, 0x009, 0x009, 0x009, 0x009, 0x009, 0x009, 0x009, 0x009, 0x009, 0x009, 0x009, - 0x009, 0x009, 0x009, 0x009, 0x009, 0x009, 0x009, 0x009, 0x009, 0x009, 0x009, 0x009, - 0x009, 0x009, 0x009, 0x009, 0x009, 0x009, 0x009, 0x009, 0x009, 0x009, 0x009, 0x009, - 0x009, 0x009, 0x009, 0x009, 0x009, 0x009, 0x009, 0x009, 0x009, 0x009, 0x009, 0x009, - 0x009, 0x009, 0x009, 0x009, 0x009, 0x009, 0x009, 0x009, 0x009, 0x009, 0x009, 0x009, - 0x009, 0x009, 0x008, 0x008, 0x008, 0x008, 0x008, 0x008, 0x008, 0x008, 0x008, 0x008, - 0x008, 0x008, 0x008, 0x008, 0x008, 0x008, 0x008, 0x008, 0x008, 0x008, 0x008, 0x008, - 0x008, 0x008, 0x008, 0x008, 0x008, 0x008, 0x008, 0x008, 0x008, 0x008, 0x008, 0x008, - 0x008, 0x008, 0x008, 0x008, 0x008, 0x008, 0x008, 0x008, 0x008, 0x008, 0x008, 0x008, - 0x008, 0x008, 0x008, 0x008, 0x008, 0x008, 0x008, 0x008, 0x008, 0x008, 0x008, 0x008, - 0x008, 0x008, 0x008, 0x008, 0x008, 0x008, 0x008, 0x008, 0x008, 0x008, 0x008, 0x008, - 0x007, 0x007, 0x007, 0x007, 0x007, 0x007, 0x007, 0x007, 0x007, 0x007, 0x007, 0x007, - 0x007, 0x007, 0x007, 0x007, 0x007, 0x007, 0x007, 0x007, 0x007, 0x007, 0x007, 0x007, - 0x007, 0x007, 0x007, 0x007, 0x007, 0x007, 0x007, 0x007, 0x007, 0x007, 0x007, 0x007, - 0x007, 0x007, 0x007, 0x007, 0x007, 0x007, 0x007, 0x007, 0x007, 0x007, 0x007, 0x007, - 0x007, 0x007, 0x007, 0x007, 0x007, 0x007, 0x007, 0x007, 0x007, 0x007, 0x007, 0x007, - 0x007, 0x007, 0x007, 0x007, 0x007, 0x007, 0x007, 0x007, 0x007, 0x007, 0x007, 0x007, - 0x007, 0x007, 0x007, 0x007, 0x007, 0x007, 0x007, 0x007, 0x006, 0x006, 0x006, 0x006, - 0x006, 0x006, 0x006, 0x006, 0x006, 0x006, 0x006, 0x006, 0x006, 0x006, 0x006, 0x006, - 0x006, 0x006, 0x006, 0x006, 0x006, 0x006, 0x006, 0x006, 0x006, 0x006, 0x006, 0x006, - 0x006, 0x006, 0x006, 0x006, 0x006, 0x006, 0x006, 0x006, 0x006, 0x006, 0x006, 0x006, - 0x006, 0x006, 0x006, 0x006, 0x006, 0x006, 0x006, 0x006, 0x006, 0x006, 0x006, 0x006, - 0x006, 0x006, 0x006, 0x006, 0x006, 0x006, 0x006, 0x006, 0x006, 0x006, 0x006, 0x006, - 0x006, 0x006, 0x006, 0x006, 0x006, 0x006, 0x006, 0x006, 0x006, 0x006, 0x006, 0x006, - 0x006, 0x006, 0x006, 0x006, 0x006, 0x006, 0x006, 0x006, 0x006, 0x006, 0x006, 0x006, - 0x006, 0x006, 0x006, 0x006, 0x006, 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, - 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, - 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, - 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, - 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, - 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, - 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, - 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, - 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, - 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, 0x004, - 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, - 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, - 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, - 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, - 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, - 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, - 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, - 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, - 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, - 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, - 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, - 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x003, - 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, - 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, - 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, - 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, - 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, - 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, - 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, - 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, - 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, - 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, - 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, - 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, - 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, - 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, - 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, - 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, - 0x003, 0x003, 0x003, 0x003, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, - 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, - 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, - 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, - 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, - 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, - 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, - 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, - 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, - 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, - 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, - 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, - 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, - 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, - 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, - 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, - 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, - 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, - 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, - 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, - 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, - 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, - 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, - 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, - 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, - 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, - 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, - 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, - 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, - 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, - 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, - 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, - 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, - 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, - 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, - 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, - 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, - 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, - 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, - 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, - 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, - 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, - 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, - 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001 -}; -} - -namespace calc_pitch -{ -uint16_t calculateFNumber(Note note, int octave, int pitch, int finePitch) -{ - uint16_t p = centTableFM[calculatePitchIndex(octave, note, pitch)]; - return (finePitch ? (finePitch > 0 ? p + finePitch : p - static_cast(-finePitch)) - : p); -} - -uint16_t calculateSSGSquareTP(Note note, int octave, int pitch, int finePitch) -{ - uint16_t p = centTableSSGSquare[calculatePitchIndex(octave, note, pitch)]; - return (finePitch ? (finePitch > 0 ? p - finePitch : p + static_cast(-finePitch)) - : p); -} - -uint16_t calculateSSGSquareTP(int n) -{ - return centTableSSGSquare[n]; -} - -uint16_t calculateSSGTriangleEP(Note note, int octave, int pitch, int finePitch) -{ - uint16_t p = centTableSSGTriangle[calculatePitchIndex(octave, note, pitch)]; - return (finePitch ? (finePitch > 0 ? p - finePitch : p + static_cast(-finePitch)) - : p); -} - -uint16_t calculateSSGSawEP(Note note, int octave, int pitch, int finePitch) -{ - uint16_t p = centTableSSGSaw[calculatePitchIndex(octave, note, pitch)]; - return (finePitch ? (finePitch > 0 ? p - finePitch : p + static_cast(-finePitch)) - : p); -} - -int calculatePitchIndex(int octave, Note note, int pitch) -{ - int idx = 384 * octave + static_cast(note) + pitch; - return clamp(idx, 0, 3071); -} -} diff -Nru bambootracker-0.4.5+git20121209/BambooTracker/calc_pitch.hpp bambootracker-0.4.6/BambooTracker/calc_pitch.hpp --- bambootracker-0.4.5+git20121209/BambooTracker/calc_pitch.hpp 2020-12-08 14:05:28.000000000 +0000 +++ bambootracker-0.4.6/BambooTracker/calc_pitch.hpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,42 +0,0 @@ -/* - * Copyright (C) 2018-2020 Rerrah - * - * Permission is hereby granted, free of charge, to any person - * obtaining a copy of this software and associated documentation - * files (the "Software"), to deal in the Software without - * restriction, including without limitation the rights to use, - * copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following - * conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES - * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT - * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, - * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR - * OTHER DEALINGS IN THE SOFTWARE. - */ - -#pragma once - -#include -#include "misc.hpp" - -namespace calc_pitch -{ -enum : int { SEMINOTE_PITCH = 32 }; - -uint16_t calculateFNumber(Note note, int octave, int calc_pitch, int finePitch); -uint16_t calculateSSGSquareTP(Note note, int octave, int calc_pitch, int finePitch); -uint16_t calculateSSGSquareTP(int n); -uint16_t calculateSSGTriangleEP(Note note, int octave, int calc_pitch, int finePitch); -uint16_t calculateSSGSawEP(Note note, int octave, int calc_pitch, int finePitch); - -int calculatePitchIndex(int octave, Note note, int calc_pitch); -} // namespace calc_pitch diff -Nru bambootracker-0.4.5+git20121209/BambooTracker/chip/codec/ymb_codec.hpp bambootracker-0.4.6/BambooTracker/chip/codec/ymb_codec.hpp --- bambootracker-0.4.5+git20121209/BambooTracker/chip/codec/ymb_codec.hpp 2020-12-08 14:05:28.000000000 +0000 +++ bambootracker-0.4.6/BambooTracker/chip/codec/ymb_codec.hpp 2021-02-11 14:37:17.000000000 +0000 @@ -39,7 +39,7 @@ return newval; } -void ymb_encode(int16_t *buffer,uint8_t *outbuffer,long len) +inline void ymb_encode(int16_t *buffer,uint8_t *outbuffer,long len) { long i; int16_t step_size = 127; @@ -64,7 +64,7 @@ } } -void ymb_decode(uint8_t *buffer,int16_t *outbuffer,long len) +inline void ymb_decode(uint8_t *buffer,int16_t *outbuffer,long len) { long i; diff -Nru bambootracker-0.4.5+git20121209/BambooTracker/chip/opna.cpp bambootracker-0.4.6/BambooTracker/chip/opna.cpp --- bambootracker-0.4.5+git20121209/BambooTracker/chip/opna.cpp 2020-12-08 14:05:28.000000000 +0000 +++ bambootracker-0.4.6/BambooTracker/chip/opna.cpp 2021-02-11 14:37:17.000000000 +0000 @@ -38,6 +38,8 @@ #include "nuked/nuke2608intf.h" } +namespace chip +{ namespace { constexpr double VOL_REDUC_ = 7.5; @@ -50,8 +52,6 @@ } } -namespace chip -{ size_t OPNA::count_ = 0; OPNA::OPNA(OpnaEmulator emu, int clock, int rate, size_t maxDuration, size_t dramSize, diff -Nru bambootracker-0.4.5+git20121209/BambooTracker/chip/opna.hpp bambootracker-0.4.6/BambooTracker/chip/opna.hpp --- bambootracker-0.4.5+git20121209/BambooTracker/chip/opna.hpp 2020-12-08 14:05:28.000000000 +0000 +++ bambootracker-0.4.6/BambooTracker/chip/opna.hpp 2021-02-11 14:37:17.000000000 +0000 @@ -49,7 +49,7 @@ Last = Nuked, }; -class OPNA : public Chip +class OPNA final : public Chip { public: // [rate] diff -Nru bambootracker-0.4.5+git20121209/BambooTracker/command/command_manager.cpp bambootracker-0.4.6/BambooTracker/command/command_manager.cpp --- bambootracker-0.4.5+git20121209/BambooTracker/command/command_manager.cpp 2020-12-08 14:05:28.000000000 +0000 +++ bambootracker-0.4.6/BambooTracker/command/command_manager.cpp 2021-02-11 14:37:17.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (C) 2018 Rerrah + * Copyright (C) 2018-2021 Rerrah * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation @@ -26,8 +26,6 @@ #include "command_manager.hpp" #include -CommandManager::CommandManager() {} - void CommandManager::invoke(CommandIPtr command) { command->redo(); diff -Nru bambootracker-0.4.5+git20121209/BambooTracker/command/command_manager.hpp bambootracker-0.4.6/BambooTracker/command/command_manager.hpp --- bambootracker-0.4.5+git20121209/BambooTracker/command/command_manager.hpp 2020-12-08 14:05:28.000000000 +0000 +++ bambootracker-0.4.6/BambooTracker/command/command_manager.hpp 2021-02-11 14:37:17.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (C) 2018 Rerrah + * Copyright (C) 2018-2021 Rerrah * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation @@ -34,13 +34,11 @@ public: using CommandIPtr = std::unique_ptr; - CommandManager(); void invoke(CommandIPtr command); void undo(); void redo(); void clear(); private: - std::stack undoStack_; - std::stack redoStack_; + std::stack undoStack_, redoStack_; }; diff -Nru bambootracker-0.4.5+git20121209/BambooTracker/command/instrument/add_instrument_command.cpp bambootracker-0.4.6/BambooTracker/command/instrument/add_instrument_command.cpp --- bambootracker-0.4.5+git20121209/BambooTracker/command/instrument/add_instrument_command.cpp 2020-12-08 14:05:28.000000000 +0000 +++ bambootracker-0.4.6/BambooTracker/command/instrument/add_instrument_command.cpp 2021-02-11 14:37:17.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (C) 2018-2020 Rerrah + * Copyright (C) 2018-2021 Rerrah * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation @@ -27,7 +27,7 @@ #include AddInstrumentCommand::AddInstrumentCommand(std::weak_ptr manager, - int num, InstrumentType type, std::string name) + int num, InstrumentType type, const std::string& name) : AbstractCommand(CommandId::AddInstrument), manager_(manager), num_(num), @@ -40,14 +40,14 @@ std::unique_ptr inst) : AbstractCommand(CommandId::AddInstrument), manager_(manager), + num_(inst->getNumber()), inst_(std::move(inst)) { - num_ = inst_->getNumber(); } void AddInstrumentCommand::redo() { - if (inst_) manager_.lock()->addInstrument(std::unique_ptr(inst_->clone())); + if (inst_) manager_.lock()->addInstrument(inst_->clone()); else manager_.lock()->addInstrument(num_, type_, name_); } diff -Nru bambootracker-0.4.5+git20121209/BambooTracker/command/instrument/add_instrument_command.hpp bambootracker-0.4.6/BambooTracker/command/instrument/add_instrument_command.hpp --- bambootracker-0.4.5+git20121209/BambooTracker/command/instrument/add_instrument_command.hpp 2020-12-08 14:05:28.000000000 +0000 +++ bambootracker-0.4.6/BambooTracker/command/instrument/add_instrument_command.hpp 2021-02-11 14:37:17.000000000 +0000 @@ -31,11 +31,11 @@ #include "instrument.hpp" #include "instruments_manager.hpp" -class AddInstrumentCommand : public AbstractCommand +class AddInstrumentCommand final : public AbstractCommand { public: AddInstrumentCommand(std::weak_ptr manager, - int num, InstrumentType type, std::string name); + int num, InstrumentType type, const std::string& name); AddInstrumentCommand(std::weak_ptr manager, std::unique_ptr inst); void redo() override; diff -Nru bambootracker-0.4.5+git20121209/BambooTracker/command/instrument/change_instrument_name_command.cpp bambootracker-0.4.6/BambooTracker/command/instrument/change_instrument_name_command.cpp --- bambootracker-0.4.5+git20121209/BambooTracker/command/instrument/change_instrument_name_command.cpp 2020-12-08 14:05:28.000000000 +0000 +++ bambootracker-0.4.6/BambooTracker/command/instrument/change_instrument_name_command.cpp 2021-02-11 14:37:17.000000000 +0000 @@ -26,7 +26,7 @@ #include "change_instrument_name_command.hpp" ChangeInstrumentNameCommand::ChangeInstrumentNameCommand(std::weak_ptr manager, - int num, std::string name) + int num, const std::string& name) : AbstractCommand(CommandId::ChangeInstrumentName), manager_(manager), instNum_(num), diff -Nru bambootracker-0.4.5+git20121209/BambooTracker/command/instrument/change_instrument_name_command.hpp bambootracker-0.4.6/BambooTracker/command/instrument/change_instrument_name_command.hpp --- bambootracker-0.4.5+git20121209/BambooTracker/command/instrument/change_instrument_name_command.hpp 2020-12-08 14:05:28.000000000 +0000 +++ bambootracker-0.4.6/BambooTracker/command/instrument/change_instrument_name_command.hpp 2021-02-11 14:37:17.000000000 +0000 @@ -30,11 +30,11 @@ #include "../abstract_command.hpp" #include "instruments_manager.hpp" -class ChangeInstrumentNameCommand : public AbstractCommand +class ChangeInstrumentNameCommand final : public AbstractCommand { public: ChangeInstrumentNameCommand(std::weak_ptr manager, - int num, std::string name); + int num, const std::string& name); void redo() override; void undo() override; diff -Nru bambootracker-0.4.5+git20121209/BambooTracker/command/instrument/clone_instrument_command.hpp bambootracker-0.4.6/BambooTracker/command/instrument/clone_instrument_command.hpp --- bambootracker-0.4.5+git20121209/BambooTracker/command/instrument/clone_instrument_command.hpp 2020-12-08 14:05:28.000000000 +0000 +++ bambootracker-0.4.6/BambooTracker/command/instrument/clone_instrument_command.hpp 2021-02-11 14:37:17.000000000 +0000 @@ -29,7 +29,7 @@ #include "../abstract_command.hpp" #include "instruments_manager.hpp" -class cloneInstrumentCommand : public AbstractCommand +class cloneInstrumentCommand final : public AbstractCommand { public: cloneInstrumentCommand(std::weak_ptr manager, int num, int refNum); diff -Nru bambootracker-0.4.5+git20121209/BambooTracker/command/instrument/deep_clone_instrument_command.hpp bambootracker-0.4.6/BambooTracker/command/instrument/deep_clone_instrument_command.hpp --- bambootracker-0.4.5+git20121209/BambooTracker/command/instrument/deep_clone_instrument_command.hpp 2020-12-08 14:05:28.000000000 +0000 +++ bambootracker-0.4.6/BambooTracker/command/instrument/deep_clone_instrument_command.hpp 2021-02-11 14:37:17.000000000 +0000 @@ -29,7 +29,7 @@ #include "../abstract_command.hpp" #include "instruments_manager.hpp" -class DeepCloneInstrumentCommand : public AbstractCommand +class DeepCloneInstrumentCommand final : public AbstractCommand { public: DeepCloneInstrumentCommand(std::weak_ptr manager, int num, int refNum); diff -Nru bambootracker-0.4.5+git20121209/BambooTracker/command/instrument/remove_instrument_command.cpp bambootracker-0.4.6/BambooTracker/command/instrument/remove_instrument_command.cpp --- bambootracker-0.4.5+git20121209/BambooTracker/command/instrument/remove_instrument_command.cpp 2020-12-08 14:05:28.000000000 +0000 +++ bambootracker-0.4.6/BambooTracker/command/instrument/remove_instrument_command.cpp 2021-02-11 14:37:17.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (C) 2018-2020 Rerrah + * Copyright (C) 2018-2021 Rerrah * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation @@ -39,5 +39,5 @@ void RemoveInstrumentCommand::undo() { - manager_.lock()->addInstrument(std::move(inst_)); + manager_.lock()->addInstrument(inst_.release()); } diff -Nru bambootracker-0.4.5+git20121209/BambooTracker/command/instrument/remove_instrument_command.hpp bambootracker-0.4.6/BambooTracker/command/instrument/remove_instrument_command.hpp --- bambootracker-0.4.5+git20121209/BambooTracker/command/instrument/remove_instrument_command.hpp 2020-12-08 14:05:28.000000000 +0000 +++ bambootracker-0.4.6/BambooTracker/command/instrument/remove_instrument_command.hpp 2021-02-11 14:37:17.000000000 +0000 @@ -30,7 +30,7 @@ #include "instruments_manager.hpp" #include "instrument.hpp" -class RemoveInstrumentCommand : public AbstractCommand +class RemoveInstrumentCommand final : public AbstractCommand { public: RemoveInstrumentCommand(std::weak_ptr manager, int number); diff -Nru bambootracker-0.4.5+git20121209/BambooTracker/command/instrument/swap_instruments_command.hpp bambootracker-0.4.6/BambooTracker/command/instrument/swap_instruments_command.hpp --- bambootracker-0.4.5+git20121209/BambooTracker/command/instrument/swap_instruments_command.hpp 2020-12-08 14:05:28.000000000 +0000 +++ bambootracker-0.4.6/BambooTracker/command/instrument/swap_instruments_command.hpp 2021-02-11 14:37:17.000000000 +0000 @@ -30,7 +30,7 @@ #include "instruments_manager.hpp" #include "module.hpp" -class SwapInstrumentsCommand : public AbstractCommand +class SwapInstrumentsCommand final : public AbstractCommand { public: SwapInstrumentsCommand(std::weak_ptr manager, std::weak_ptr mod, diff -Nru bambootracker-0.4.5+git20121209/BambooTracker/command/order/clone_order_command.cpp bambootracker-0.4.6/BambooTracker/command/order/clone_order_command.cpp --- bambootracker-0.4.5+git20121209/BambooTracker/command/order/clone_order_command.cpp 2020-12-08 14:05:28.000000000 +0000 +++ bambootracker-0.4.6/BambooTracker/command/order/clone_order_command.cpp 2021-02-11 14:37:17.000000000 +0000 @@ -38,7 +38,7 @@ auto& track = sng.getTrack(t.number); // Set previous pattern to avoid leaving unused pattern track.registerPatternToOrder(order_ + 1, track.getPatternFromOrderNumber(order_).getNumber()); - track.registerPatternToOrder(order_ + 1, track.clonePattern(track.getOrderData(order_).patten)); + track.registerPatternToOrder(order_ + 1, track.clonePattern(track.getOrderInfo(order_).patten)); } } diff -Nru bambootracker-0.4.5+git20121209/BambooTracker/command/order/clone_order_command.hpp bambootracker-0.4.6/BambooTracker/command/order/clone_order_command.hpp --- bambootracker-0.4.5+git20121209/BambooTracker/command/order/clone_order_command.hpp 2020-12-08 14:05:28.000000000 +0000 +++ bambootracker-0.4.6/BambooTracker/command/order/clone_order_command.hpp 2021-02-11 14:37:17.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (C) 2018-2019 Rerrah + * Copyright (C) 2018-2020 Rerrah * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation @@ -30,7 +30,7 @@ #include "../abstract_command.hpp" #include "module.hpp" -class CloneOrderCommand : public AbstractCommand +class CloneOrderCommand final : public AbstractCommand { public: CloneOrderCommand(std::weak_ptr mod, int songNum, int orderNum); @@ -40,5 +40,5 @@ private: std::weak_ptr mod_; int song_, order_; - std::vector prevOdr_; + std::vector prevOdr_; }; diff -Nru bambootracker-0.4.5+git20121209/BambooTracker/command/order/clone_patterns_command.cpp bambootracker-0.4.6/BambooTracker/command/order/clone_patterns_command.cpp --- bambootracker-0.4.5+git20121209/BambooTracker/command/order/clone_patterns_command.cpp 2020-12-08 14:05:28.000000000 +0000 +++ bambootracker-0.4.6/BambooTracker/command/order/clone_patterns_command.cpp 2021-02-11 14:37:17.000000000 +0000 @@ -39,7 +39,7 @@ prevOdrs_.emplace_back(); for (int t = beginTrack; t <= endTrack; ++t) { prevOdrs_.at(static_cast(o - beginOrder)).push_back( - mod_.lock()->getSong(songNum).getTrack(t).getOrderData(o)); + mod_.lock()->getSong(songNum).getTrack(t).getOrderInfo(o)); } } } @@ -50,7 +50,7 @@ for (int o = bOrder_; o <= eOrder_; ++o) { for (int t = bTrack_; t <= eTrack_; ++t) { auto& track = sng.getTrack(t); - track.registerPatternToOrder(o, track.clonePattern(track.getOrderData(o).patten)); + track.registerPatternToOrder(o, track.clonePattern(track.getOrderInfo(o).patten)); } } } diff -Nru bambootracker-0.4.5+git20121209/BambooTracker/command/order/clone_patterns_command.hpp bambootracker-0.4.6/BambooTracker/command/order/clone_patterns_command.hpp --- bambootracker-0.4.5+git20121209/BambooTracker/command/order/clone_patterns_command.hpp 2020-12-08 14:05:28.000000000 +0000 +++ bambootracker-0.4.6/BambooTracker/command/order/clone_patterns_command.hpp 2021-02-11 14:37:17.000000000 +0000 @@ -30,7 +30,7 @@ #include "../abstract_command.hpp" #include "module.hpp" -class ClonePatternsCommand : public AbstractCommand +class ClonePatternsCommand final : public AbstractCommand { public: ClonePatternsCommand(std::weak_ptr mod, int songNum, @@ -41,5 +41,5 @@ private: std::weak_ptr mod_; int song_, bOrder_, bTrack_, eOrder_, eTrack_; - std::vector> prevOdrs_; + std::vector> prevOdrs_; }; diff -Nru bambootracker-0.4.5+git20121209/BambooTracker/command/order/delete_order_command.hpp bambootracker-0.4.6/BambooTracker/command/order/delete_order_command.hpp --- bambootracker-0.4.5+git20121209/BambooTracker/command/order/delete_order_command.hpp 2020-12-08 14:05:28.000000000 +0000 +++ bambootracker-0.4.6/BambooTracker/command/order/delete_order_command.hpp 2021-02-11 14:37:17.000000000 +0000 @@ -30,7 +30,7 @@ #include "../abstract_command.hpp" #include "module.hpp" -class DeleteOrderCommand : public AbstractCommand +class DeleteOrderCommand final : public AbstractCommand { public: DeleteOrderCommand(std::weak_ptr mod, int songNum, int orderNum); @@ -40,5 +40,5 @@ private: std::weak_ptr mod_; int song_, order_; - std::vector prevOdr_; + std::vector prevOdr_; }; diff -Nru bambootracker-0.4.5+git20121209/BambooTracker/command/order/duplicate_order_command.cpp bambootracker-0.4.6/BambooTracker/command/order/duplicate_order_command.cpp --- bambootracker-0.4.5+git20121209/BambooTracker/command/order/duplicate_order_command.cpp 2020-12-08 14:05:28.000000000 +0000 +++ bambootracker-0.4.6/BambooTracker/command/order/duplicate_order_command.cpp 2021-02-11 14:37:17.000000000 +0000 @@ -36,7 +36,7 @@ sng.insertOrderBelow(order_); for (auto& t : sng.getTrackAttributes()) { auto& track = sng.getTrack(t.number); - track.registerPatternToOrder(order_ + 1, track.getOrderData(order_).patten); + track.registerPatternToOrder(order_ + 1, track.getOrderInfo(order_).patten); } } diff -Nru bambootracker-0.4.5+git20121209/BambooTracker/command/order/insert_order_below_command.hpp bambootracker-0.4.6/BambooTracker/command/order/insert_order_below_command.hpp --- bambootracker-0.4.5+git20121209/BambooTracker/command/order/insert_order_below_command.hpp 2020-12-08 14:05:28.000000000 +0000 +++ bambootracker-0.4.6/BambooTracker/command/order/insert_order_below_command.hpp 2021-02-11 14:37:17.000000000 +0000 @@ -29,7 +29,7 @@ #include "../abstract_command.hpp" #include "module.hpp" -class InsertOrderBelowCommand : public AbstractCommand +class InsertOrderBelowCommand final : public AbstractCommand { public: InsertOrderBelowCommand(std::weak_ptr mod, int songNum, int orderNum); diff -Nru bambootracker-0.4.5+git20121209/BambooTracker/command/order/move_order_command.hpp bambootracker-0.4.6/BambooTracker/command/order/move_order_command.hpp --- bambootracker-0.4.5+git20121209/BambooTracker/command/order/move_order_command.hpp 2020-12-08 14:05:28.000000000 +0000 +++ bambootracker-0.4.6/BambooTracker/command/order/move_order_command.hpp 2021-02-11 14:37:17.000000000 +0000 @@ -29,7 +29,7 @@ #include "../abstract_command.hpp" #include "module.hpp" -class MoveOrderCommand : public AbstractCommand +class MoveOrderCommand final : public AbstractCommand { public: MoveOrderCommand(std::weak_ptr mod, int songNum, int orderNum, bool isUp); diff -Nru bambootracker-0.4.5+git20121209/BambooTracker/command/order/paste_copied_data_to_order_command.cpp bambootracker-0.4.6/BambooTracker/command/order/paste_copied_data_to_order_command.cpp --- bambootracker-0.4.5+git20121209/BambooTracker/command/order/paste_copied_data_to_order_command.cpp 2020-12-08 14:05:28.000000000 +0000 +++ bambootracker-0.4.6/BambooTracker/command/order/paste_copied_data_to_order_command.cpp 2021-02-11 14:37:17.000000000 +0000 @@ -26,8 +26,9 @@ #include "paste_copied_data_to_order_command.hpp" #include "track.hpp" -PasteCopiedDataToOrderCommand::PasteCopiedDataToOrderCommand(std::weak_ptr mod, int songNum, int beginTrack, int beginOrder, - std::vector> cells) +PasteCopiedDataToOrderCommand::PasteCopiedDataToOrderCommand( + std::weak_ptr mod, int songNum, int beginTrack, int beginOrder, + const std::vector>& cells) : AbstractCommand(CommandId::PasteCopiedDataToOrder), mod_(mod), song_(songNum), @@ -38,7 +39,7 @@ auto& sng = mod.lock()->getSong(songNum); for (size_t i = 0; i < cells.size(); ++i) { prevCells_.emplace_back(); - std::vector odrs = sng.getOrderData(beginOrder + static_cast(i)); + std::vector odrs = sng.getOrderData(beginOrder + static_cast(i)); for (size_t j = 0; j < cells.at(i).size(); ++j) { prevCells_.at(i).push_back(std::to_string(odrs.at(static_cast(beginTrack) + j).patten)); } @@ -55,7 +56,7 @@ setCells(prevCells_); } -void PasteCopiedDataToOrderCommand::setCells(std::vector>& cells) +void PasteCopiedDataToOrderCommand::setCells(const std::vector>& cells) { auto& sng = mod_.lock()->getSong(song_); diff -Nru bambootracker-0.4.5+git20121209/BambooTracker/command/order/paste_copied_data_to_order_command.hpp bambootracker-0.4.6/BambooTracker/command/order/paste_copied_data_to_order_command.hpp --- bambootracker-0.4.5+git20121209/BambooTracker/command/order/paste_copied_data_to_order_command.hpp 2020-12-08 14:05:28.000000000 +0000 +++ bambootracker-0.4.6/BambooTracker/command/order/paste_copied_data_to_order_command.hpp 2021-02-11 14:37:17.000000000 +0000 @@ -31,11 +31,12 @@ #include "../abstract_command.hpp" #include "module.hpp" -class PasteCopiedDataToOrderCommand : public AbstractCommand +class PasteCopiedDataToOrderCommand final : public AbstractCommand { public: - PasteCopiedDataToOrderCommand(std::weak_ptr mod, int songNum, int beginTrack, int beginOrder, - std::vector> cells); + PasteCopiedDataToOrderCommand( + std::weak_ptr mod, int songNum, int beginTrack, int beginOrder, + const std::vector>& cells); void redo() override; void undo() override; @@ -44,5 +45,5 @@ int song_, track_, order_; std::vector> cells_, prevCells_; - void setCells(std::vector>& cells); + void setCells(const std::vector>& cells); }; diff -Nru bambootracker-0.4.5+git20121209/BambooTracker/command/order/set_pattern_to_order_command.hpp bambootracker-0.4.6/BambooTracker/command/order/set_pattern_to_order_command.hpp --- bambootracker-0.4.5+git20121209/BambooTracker/command/order/set_pattern_to_order_command.hpp 2020-12-08 14:05:28.000000000 +0000 +++ bambootracker-0.4.6/BambooTracker/command/order/set_pattern_to_order_command.hpp 2021-02-11 14:37:17.000000000 +0000 @@ -29,7 +29,7 @@ #include "../abstract_command.hpp" #include "module.hpp" -class SetPatternToOrderCommand : public AbstractCommand +class SetPatternToOrderCommand final : public AbstractCommand { public: SetPatternToOrderCommand(std::weak_ptr mod, int songNum, int trackNum, diff -Nru bambootracker-0.4.5+git20121209/BambooTracker/command/pattern/change_values_in_pattern_command.cpp bambootracker-0.4.6/BambooTracker/command/pattern/change_values_in_pattern_command.cpp --- bambootracker-0.4.5+git20121209/BambooTracker/command/pattern/change_values_in_pattern_command.cpp 2020-12-08 14:05:28.000000000 +0000 +++ bambootracker-0.4.6/BambooTracker/command/pattern/change_values_in_pattern_command.cpp 2021-02-11 14:37:17.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (C) 2020 Rerrah + * Copyright (C) 2020-2021 Rerrah * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation @@ -25,11 +25,12 @@ #include "change_values_in_pattern_command.hpp" #include "pattern_command_utils.hpp" -#include "misc.hpp" +#include "bamboo_tracker_defs.hpp" +#include "utils.hpp" -ChangeValuesInPatternCommand::ChangeValuesInPatternCommand(std::weak_ptr mod, int songNum, int beginTrack, - int beginColumn, int beginOrder, int beginStep, - int endTrack, int endColumn, int endStep, int value, bool isFMReversed) +ChangeValuesInPatternCommand::ChangeValuesInPatternCommand( + std::weak_ptr mod, int songNum, int beginTrack, int beginColumn, int beginOrder, + int beginStep, int endTrack, int endColumn, int endStep, int value, bool isFMReversed) : AbstractCommand(CommandId::ChangeValuesInPattern), mod_(mod), song_(songNum), @@ -50,21 +51,27 @@ int col = beginColumn; std::vector vals; while (true) { - int val; Step& st = command_utils::getStep(sng, track, beginOrder, step); switch (col) { - case 1: val = st.getInstrumentNumber(); break; - case 2: val = st.getVolume(); break; - case 4: val = st.getEffectValue(0); break; - case 6: val = st.getEffectValue(1); break; - case 8: val = st.getEffectValue(2); break; - case 10: val = st.getEffectValue(3); break; - default: val = -1; break; + case 1: + if (st.hasInstrument()) vals.push_back(st.getInstrumentNumber()); + break; + case 2: + if (st.hasVolume()) vals.push_back(st.getVolume()); + break; + default: + { + if (col) { + int ec = col - 3; + int ei = ec / 2; + if (ec % 2 && st.hasEffectValue(ei)) vals.push_back(st.getEffectValue(ei)); + } + break; + } } - if (val > -1) vals.push_back(val); if (track == endTrack && col == endColumn) break; - track += (++col / 11); - col %= 11; + track += (++col / Step::N_COLUMN); + col %= Step::N_COLUMN; } prevVals_.push_back(vals); } @@ -83,32 +90,30 @@ Step& st = tr.getPatternFromOrderNumber(order_).getStep(step); switch (col) { case 1: - if (st.getInstrumentNumber() > -1) st.setInstrumentNumber(clamp(diff_ + *valit++, 0, 127)); + if (st.hasInstrument()) + st.setInstrumentNumber(utils::clamp(diff_ + *valit++, 0, 127)); break; case 2: - if (st.getVolume() > -1) { - int d = (tr.getAttribute().source == SoundSource::FM && fmReverse_) ? -diff_ : diff_; - st.setVolume(clamp(d + *valit++, 0, 255)); + if (st.hasVolume()) { + int d = (tr.getAttribute().source == SoundSource::FM && fmReverse_) ? -diff_ + : diff_; + st.setVolume(utils::clamp(d + *valit++, 0, 255)); } break; - case 4: - if (st.getEffectValue(0) > -1) st.setEffectValue(0, clamp(diff_ + *valit++, 0, 255)); - break; - case 6: - if (st.getEffectValue(1) > -1) st.setEffectValue(1, clamp(diff_ + *valit++, 0, 255)); - break; - case 8: - if (st.getEffectValue(2) > -1) st.setEffectValue(2, clamp(diff_ + *valit++, 0, 255)); - break; - case 10: - if (st.getEffectValue(3) > -1) st.setEffectValue(3, clamp(diff_ + *valit++, 0, 255)); - break; default: + { + if (col) { + int ec = col - 3; + int ei = ec / 2; + if (ec % 2 && st.hasEffectValue(ei)) + st.setEffectValue(ei, utils::clamp(diff_ + *valit++, 0, 255)); + } break; } + } if (track == eTrack_ && col == eCol_) break; - track += (++col / 11); - col %= 11; + track += (++col / Step::N_COLUMN); + col %= Step::N_COLUMN; } } } @@ -125,29 +130,24 @@ Step& st = command_utils::getStep(sng, track, order_, step); switch (col) { case 1: - if (st.getInstrumentNumber() > -1) st.setInstrumentNumber(*valit++); + if (st.hasInstrument()) st.setInstrumentNumber(*valit++); break; case 2: - if (st.getVolume() > -1) st.setVolume(*valit++); - break; - case 4: - if (st.getEffectValue(0) > -1) st.setEffectValue(0, *valit++); - break; - case 6: - if (st.getEffectValue(1) > -1) st.setEffectValue(1, *valit++); - break; - case 8: - if (st.getEffectValue(2) > -1) st.setEffectValue(2, *valit++); - break; - case 10: - if (st.getEffectValue(3) > -1) st.setEffectValue(3, *valit++); + if (st.hasVolume()) st.setVolume(*valit++); break; default: + { + if (col) { + int ec = col - 3; + int ei = ec / 2; + if (ec % 2 && st.hasEffectValue(ei)) st.setEffectValue(ei, *valit++); + } break; } + } if (track == eTrack_ && col == eCol_) break; - track += (++col / 11); - col %= 11; + track += (++col / Step::N_COLUMN); + col %= Step::N_COLUMN; } } } diff -Nru bambootracker-0.4.5+git20121209/BambooTracker/command/pattern/change_values_in_pattern_command.hpp bambootracker-0.4.6/BambooTracker/command/pattern/change_values_in_pattern_command.hpp --- bambootracker-0.4.5+git20121209/BambooTracker/command/pattern/change_values_in_pattern_command.hpp 2020-12-08 14:05:28.000000000 +0000 +++ bambootracker-0.4.6/BambooTracker/command/pattern/change_values_in_pattern_command.hpp 2021-02-11 14:37:17.000000000 +0000 @@ -30,7 +30,7 @@ #include "../abstract_command.hpp" #include "module.hpp" -class ChangeValuesInPatternCommand : public AbstractCommand +class ChangeValuesInPatternCommand final : public AbstractCommand { public: ChangeValuesInPatternCommand(std::weak_ptr mod, int songNum, diff -Nru bambootracker-0.4.5+git20121209/BambooTracker/command/pattern/delete_previous_step_command.cpp bambootracker-0.4.6/BambooTracker/command/pattern/delete_previous_step_command.cpp --- bambootracker-0.4.5+git20121209/BambooTracker/command/pattern/delete_previous_step_command.cpp 2020-12-08 14:05:28.000000000 +0000 +++ bambootracker-0.4.6/BambooTracker/command/pattern/delete_previous_step_command.cpp 2021-02-11 14:37:17.000000000 +0000 @@ -26,8 +26,8 @@ #include "delete_previous_step_command.hpp" #include "pattern_command_utils.hpp" -DeletePreviousStepCommand::DeletePreviousStepCommand(std::weak_ptr mod, int songNum, - int trackNum, int orderNum, int stepNum) +DeletePreviousStepCommand::DeletePreviousStepCommand( + std::weak_ptr mod, int songNum, int trackNum, int orderNum, int stepNum) : AbstractCommand(CommandId::DeletePreviousStep), mod_(mod), song_(songNum), @@ -39,9 +39,8 @@ prevNote_ = st.getNoteNumber(); prevInst_ = st.getInstrumentNumber(); prevVol_ = st.getVolume(); - for (int i = 0; i < 4; ++i) { - prevEffID_[i] = st.getEffectID(i); - prevEffVal_[i] = st.getEffectValue(i); + for (int i = 0; i < Step::N_EFFECT; ++i) { + prevEff_[i] = st.getEffect(i); } } @@ -58,8 +57,7 @@ st.setNoteNumber(prevNote_); st.setInstrumentNumber(prevInst_); st.setVolume(prevVol_); - for (int i = 0; i < 4; ++i) { - st.setEffectID(i, prevEffID_[i]); - st.setEffectValue(i, prevEffVal_[i]); + for (int i = 0; i < Step::N_EFFECT; ++i) { + st.setEffect(i, prevEff_[i]); } } diff -Nru bambootracker-0.4.5+git20121209/BambooTracker/command/pattern/delete_previous_step_command.hpp bambootracker-0.4.6/BambooTracker/command/pattern/delete_previous_step_command.hpp --- bambootracker-0.4.5+git20121209/BambooTracker/command/pattern/delete_previous_step_command.hpp 2020-12-08 14:05:28.000000000 +0000 +++ bambootracker-0.4.6/BambooTracker/command/pattern/delete_previous_step_command.hpp 2021-02-11 14:37:17.000000000 +0000 @@ -30,7 +30,7 @@ #include "../abstract_command.hpp" #include "module.hpp" -class DeletePreviousStepCommand : public AbstractCommand +class DeletePreviousStepCommand final : public AbstractCommand { public: DeletePreviousStepCommand(std::weak_ptr mod, int songNum, int trackNum, @@ -41,6 +41,6 @@ private: std::weak_ptr mod_; int song_, track_, order_, step_; - int prevNote_, prevInst_, prevVol_, prevEffVal_[4]; - std::string prevEffID_[4]; + int prevNote_, prevInst_, prevVol_; + Step::PlainEffect prevEff_[Step::N_EFFECT]; }; diff -Nru bambootracker-0.4.5+git20121209/BambooTracker/command/pattern/erase_cells_in_pattern_command.cpp bambootracker-0.4.6/BambooTracker/command/pattern/erase_cells_in_pattern_command.cpp --- bambootracker-0.4.5+git20121209/BambooTracker/command/pattern/erase_cells_in_pattern_command.cpp 2020-12-08 14:05:28.000000000 +0000 +++ bambootracker-0.4.6/BambooTracker/command/pattern/erase_cells_in_pattern_command.cpp 2021-02-11 14:37:17.000000000 +0000 @@ -26,10 +26,9 @@ #include "erase_cells_in_pattern_command.hpp" #include "pattern_command_utils.hpp" -EraseCellsInPatternCommand::EraseCellsInPatternCommand(std::weak_ptr mod, - int songNum, int beginTrack, int beginColumn, - int beginOrder, int beginStep, - int endTrack, int endColumn, int endStep) +EraseCellsInPatternCommand::EraseCellsInPatternCommand( + std::weak_ptr mod, int songNum, int beginTrack, int beginColumn, + int beginOrder, int beginStep, int endTrack, int endColumn, int endStep) : AbstractCommand(CommandId::EraseCellsInPattern), mod_(mod), song_(songNum), @@ -55,21 +54,21 @@ for (size_t j = 0; j < prevCells_.at(i).size(); ++j) { Step& st = command_utils::getStep(sng, t, order_, s); switch (c) { - case 0: st.setNoteNumber(-1); break; - case 1: st.setInstrumentNumber(-1); break; - case 2: st.setVolume(-1); break; - case 3: st.setEffectID(0, "--"); break; - case 4: st.setEffectValue(0, -1); break; - case 5: st.setEffectID(1, "--"); break; - case 6: st.setEffectValue(1, -1); break; - case 7: st.setEffectID(2, "--"); break; - case 8: st.setEffectValue(2, -1); break; - case 9: st.setEffectID(3, "--"); break; - case 10: st.setEffectValue(3, -1); break; + case 0: st.clearNoteNumber(); break; + case 1: st.clearInstrumentNumber(); break; + case 2: st.clearVolume(); break; + default: + { + int ec = c - 3; + int ei = ec / 2; + if (ec % 2) st.clearEffectValue(ei); + else st.clearEffectId(ei); + break; + } } - t += (++c / 11); - c %= 11; + t += (++c / Step::N_COLUMN); + c %= Step::N_COLUMN; } ++s; diff -Nru bambootracker-0.4.5+git20121209/BambooTracker/command/pattern/erase_cells_in_pattern_command.hpp bambootracker-0.4.6/BambooTracker/command/pattern/erase_cells_in_pattern_command.hpp --- bambootracker-0.4.5+git20121209/BambooTracker/command/pattern/erase_cells_in_pattern_command.hpp 2020-12-08 14:05:28.000000000 +0000 +++ bambootracker-0.4.6/BambooTracker/command/pattern/erase_cells_in_pattern_command.hpp 2021-02-11 14:37:17.000000000 +0000 @@ -31,7 +31,7 @@ #include "../abstract_command.hpp" #include "module.hpp" -class EraseCellsInPatternCommand : public AbstractCommand +class EraseCellsInPatternCommand final : public AbstractCommand { public: EraseCellsInPatternCommand(std::weak_ptr mod, int songNum, diff -Nru bambootracker-0.4.5+git20121209/BambooTracker/command/pattern/erase_effect_in_step_command.cpp bambootracker-0.4.6/BambooTracker/command/pattern/erase_effect_in_step_command.cpp --- bambootracker-0.4.5+git20121209/BambooTracker/command/pattern/erase_effect_in_step_command.cpp 2020-12-08 14:05:28.000000000 +0000 +++ bambootracker-0.4.6/BambooTracker/command/pattern/erase_effect_in_step_command.cpp 2021-02-11 14:37:17.000000000 +0000 @@ -26,8 +26,8 @@ #include "erase_effect_in_step_command.hpp" #include "pattern_command_utils.hpp" -EraseEffectInStepCommand::EraseEffectInStepCommand(std::weak_ptr mod, int songNum, - int trackNum, int orderNum, int stepNum, int n) +EraseEffectInStepCommand::EraseEffectInStepCommand( + std::weak_ptr mod, int songNum, int trackNum, int orderNum, int stepNum, int n) : AbstractCommand(CommandId::EraseEffectInStep), mod_(mod), song_(songNum), @@ -36,21 +36,15 @@ step_(stepNum), n_(n) { - Step& st = command_utils::getStep(mod, songNum, trackNum, orderNum, stepNum); - prevEffID_ = st.getEffectID(n); - prevEffVal_ = st.getEffectValue(n); + prevEff_ = command_utils::getStep(mod, songNum, trackNum, orderNum, stepNum).getEffect(n); } void EraseEffectInStepCommand::redo() { - Step& st = command_utils::getStep(mod_, song_, track_, order_, step_); - st.setEffectID(n_, "--"); - st.setEffectValue(n_, -1); + command_utils::getStep(mod_, song_, track_, order_, step_).clearEffect(n_); } void EraseEffectInStepCommand::undo() { - Step& st = command_utils::getStep(mod_, song_, track_, order_, step_); - st.setEffectID(n_, prevEffID_); - st.setEffectValue(n_, prevEffVal_); + command_utils::getStep(mod_, song_, track_, order_, step_).setEffect(n_, prevEff_); } diff -Nru bambootracker-0.4.5+git20121209/BambooTracker/command/pattern/erase_effect_in_step_command.hpp bambootracker-0.4.6/BambooTracker/command/pattern/erase_effect_in_step_command.hpp --- bambootracker-0.4.5+git20121209/BambooTracker/command/pattern/erase_effect_in_step_command.hpp 2020-12-08 14:05:28.000000000 +0000 +++ bambootracker-0.4.6/BambooTracker/command/pattern/erase_effect_in_step_command.hpp 2021-02-11 14:37:17.000000000 +0000 @@ -30,7 +30,7 @@ #include "../abstract_command.hpp" #include "module.hpp" -class EraseEffectInStepCommand : public AbstractCommand +class EraseEffectInStepCommand final : public AbstractCommand { public: EraseEffectInStepCommand(std::weak_ptr mod, int songNum, int trackNum, @@ -41,6 +41,5 @@ private: std::weak_ptr mod_; int song_, track_, order_, step_, n_; - std::string prevEffID_; - int prevEffVal_; + Step::PlainEffect prevEff_; }; diff -Nru bambootracker-0.4.5+git20121209/BambooTracker/command/pattern/erase_effect_value_in_step_command.cpp bambootracker-0.4.6/BambooTracker/command/pattern/erase_effect_value_in_step_command.cpp --- bambootracker-0.4.5+git20121209/BambooTracker/command/pattern/erase_effect_value_in_step_command.cpp 2020-12-08 14:05:28.000000000 +0000 +++ bambootracker-0.4.6/BambooTracker/command/pattern/erase_effect_value_in_step_command.cpp 2021-02-11 14:37:17.000000000 +0000 @@ -41,7 +41,7 @@ void EraseEffectValueInStepCommand::redo() { - command_utils::getStep(mod_, song_, track_, order_, step_).setEffectValue(n_, -1); + command_utils::getStep(mod_, song_, track_, order_, step_).clearEffectValue(n_); } void EraseEffectValueInStepCommand::undo() diff -Nru bambootracker-0.4.5+git20121209/BambooTracker/command/pattern/erase_effect_value_in_step_command.hpp bambootracker-0.4.6/BambooTracker/command/pattern/erase_effect_value_in_step_command.hpp --- bambootracker-0.4.5+git20121209/BambooTracker/command/pattern/erase_effect_value_in_step_command.hpp 2020-12-08 14:05:28.000000000 +0000 +++ bambootracker-0.4.6/BambooTracker/command/pattern/erase_effect_value_in_step_command.hpp 2021-02-11 14:37:17.000000000 +0000 @@ -29,7 +29,7 @@ #include "../abstract_command.hpp" #include "module.hpp" -class EraseEffectValueInStepCommand : public AbstractCommand +class EraseEffectValueInStepCommand final : public AbstractCommand { public: EraseEffectValueInStepCommand(std::weak_ptr mod, int songNum, int trackNum, diff -Nru bambootracker-0.4.5+git20121209/BambooTracker/command/pattern/erase_instrument_in_step_command.cpp bambootracker-0.4.6/BambooTracker/command/pattern/erase_instrument_in_step_command.cpp --- bambootracker-0.4.5+git20121209/BambooTracker/command/pattern/erase_instrument_in_step_command.cpp 2020-12-08 14:05:28.000000000 +0000 +++ bambootracker-0.4.6/BambooTracker/command/pattern/erase_instrument_in_step_command.cpp 2021-02-11 14:37:17.000000000 +0000 @@ -41,7 +41,7 @@ void EraseInstrumentInStepCommand::redo() { - command_utils::getStep(mod_, song_, track_, order_, step_).setInstrumentNumber(-1); + command_utils::getStep(mod_, song_, track_, order_, step_).clearInstrumentNumber(); } void EraseInstrumentInStepCommand::undo() diff -Nru bambootracker-0.4.5+git20121209/BambooTracker/command/pattern/erase_instrument_in_step_command.hpp bambootracker-0.4.6/BambooTracker/command/pattern/erase_instrument_in_step_command.hpp --- bambootracker-0.4.5+git20121209/BambooTracker/command/pattern/erase_instrument_in_step_command.hpp 2020-12-08 14:05:28.000000000 +0000 +++ bambootracker-0.4.6/BambooTracker/command/pattern/erase_instrument_in_step_command.hpp 2021-02-11 14:37:17.000000000 +0000 @@ -29,7 +29,7 @@ #include "../abstract_command.hpp" #include "module.hpp" -class EraseInstrumentInStepCommand : public AbstractCommand +class EraseInstrumentInStepCommand final : public AbstractCommand { public: EraseInstrumentInStepCommand(std::weak_ptr mod, int songNum, int trackNum, diff -Nru bambootracker-0.4.5+git20121209/BambooTracker/command/pattern/erase_step_command.cpp bambootracker-0.4.6/BambooTracker/command/pattern/erase_step_command.cpp --- bambootracker-0.4.5+git20121209/BambooTracker/command/pattern/erase_step_command.cpp 2020-12-08 14:05:28.000000000 +0000 +++ bambootracker-0.4.6/BambooTracker/command/pattern/erase_step_command.cpp 2021-02-11 14:37:17.000000000 +0000 @@ -26,7 +26,8 @@ #include "erase_step_command.hpp" #include "pattern_command_utils.hpp" -EraseStepCommand::EraseStepCommand(std::weak_ptr mod, int songNum, int trackNum, int orderNum, int stepNum) +EraseStepCommand::EraseStepCommand( + std::weak_ptr mod, int songNum, int trackNum, int orderNum, int stepNum) : AbstractCommand(CommandId::EraseStep), mod_(mod), song_(songNum), @@ -38,22 +39,14 @@ prevNote_ = st.getNoteNumber(); prevInst_ = st.getInstrumentNumber(); prevVol_ = st.getVolume(); - for (int i = 0; i < 4; ++i) { - prevEffID_[i] = st.getEffectID(i); - prevEffVal_[i] = st.getEffectValue(i); + for (int i = 0; i < Step::N_EFFECT; ++i) { + prevEff_[i] = st.getEffect(i); } } void EraseStepCommand::redo() { - Step& st = command_utils::getStep(mod_, song_, track_, order_, step_); - st.setNoteNumber(-1); - st.setInstrumentNumber(-1); - st.setVolume(-1); - for (int i = 0; i < 4; ++i){ - st.setEffectID(i, "--"); - st.setEffectValue(i, -1); - } + command_utils::getStep(mod_, song_, track_, order_, step_).clear(); } void EraseStepCommand::undo() @@ -62,8 +55,7 @@ st.setNoteNumber(prevNote_); st.setInstrumentNumber(prevInst_); st.setVolume(prevVol_); - for (int i = 0; i < 4; ++i) { - st.setEffectID(i, prevEffID_[i]); - st.setEffectValue(i, prevEffVal_[i]); + for (int i = 0; i < Step::N_EFFECT; ++i) { + st.setEffect(i, prevEff_[i]); } } diff -Nru bambootracker-0.4.5+git20121209/BambooTracker/command/pattern/erase_step_command.hpp bambootracker-0.4.6/BambooTracker/command/pattern/erase_step_command.hpp --- bambootracker-0.4.5+git20121209/BambooTracker/command/pattern/erase_step_command.hpp 2020-12-08 14:05:28.000000000 +0000 +++ bambootracker-0.4.6/BambooTracker/command/pattern/erase_step_command.hpp 2021-02-11 14:37:17.000000000 +0000 @@ -30,7 +30,7 @@ #include "../abstract_command.hpp" #include "module.hpp" -class EraseStepCommand : public AbstractCommand +class EraseStepCommand final : public AbstractCommand { public: EraseStepCommand(std::weak_ptr mod, int songNum, int trackNum, int orderNum, int stepNum); @@ -40,6 +40,6 @@ private: std::weak_ptr mod_; int song_, track_, order_, step_; - int prevNote_, prevInst_, prevVol_, prevEffVal_[4]; - std::string prevEffID_[4]; + int prevNote_, prevInst_, prevVol_; + Step::PlainEffect prevEff_[Step::N_EFFECT]; }; diff -Nru bambootracker-0.4.5+git20121209/BambooTracker/command/pattern/erase_volume_in_step_command.cpp bambootracker-0.4.6/BambooTracker/command/pattern/erase_volume_in_step_command.cpp --- bambootracker-0.4.5+git20121209/BambooTracker/command/pattern/erase_volume_in_step_command.cpp 2020-12-08 14:05:28.000000000 +0000 +++ bambootracker-0.4.6/BambooTracker/command/pattern/erase_volume_in_step_command.cpp 2021-02-11 14:37:17.000000000 +0000 @@ -26,7 +26,8 @@ #include "erase_volume_in_step_command.hpp" #include "pattern_command_utils.hpp" -EraseVolumeInStepCommand::EraseVolumeInStepCommand(std::weak_ptr mod, int songNum, int trackNum, int orderNum, int stepNum) +EraseVolumeInStepCommand::EraseVolumeInStepCommand( + std::weak_ptr mod, int songNum, int trackNum, int orderNum, int stepNum) : AbstractCommand(CommandId::EraseVolumeInStep), mod_(mod), song_(songNum), @@ -39,7 +40,7 @@ void EraseVolumeInStepCommand::redo() { - command_utils::getStep(mod_, song_, track_, order_, step_).setVolume(-1); + command_utils::getStep(mod_, song_, track_, order_, step_).clearVolume(); } void EraseVolumeInStepCommand::undo() diff -Nru bambootracker-0.4.5+git20121209/BambooTracker/command/pattern/erase_volume_in_step_command.hpp bambootracker-0.4.6/BambooTracker/command/pattern/erase_volume_in_step_command.hpp --- bambootracker-0.4.5+git20121209/BambooTracker/command/pattern/erase_volume_in_step_command.hpp 2020-12-08 14:05:28.000000000 +0000 +++ bambootracker-0.4.6/BambooTracker/command/pattern/erase_volume_in_step_command.hpp 2021-02-11 14:37:17.000000000 +0000 @@ -29,7 +29,7 @@ #include "../abstract_command.hpp" #include "module.hpp" -class EraseVolumeInStepCommand : public AbstractCommand +class EraseVolumeInStepCommand final : public AbstractCommand { public: EraseVolumeInStepCommand(std::weak_ptr mod, int songNum, int trackNum, diff -Nru bambootracker-0.4.5+git20121209/BambooTracker/command/pattern/expand_pattern_command.cpp bambootracker-0.4.6/BambooTracker/command/pattern/expand_pattern_command.cpp --- bambootracker-0.4.5+git20121209/BambooTracker/command/pattern/expand_pattern_command.cpp 2020-12-08 14:05:28.000000000 +0000 +++ bambootracker-0.4.6/BambooTracker/command/pattern/expand_pattern_command.cpp 2021-02-11 14:37:17.000000000 +0000 @@ -26,10 +26,9 @@ #include "expand_pattern_command.hpp" #include "pattern_command_utils.hpp" -ExpandPatternCommand::ExpandPatternCommand(std::weak_ptr mod, - int songNum, int beginTrack, int beginColumn, - int beginOrder, int beginStep, - int endTrack, int endColumn, int endStep) +ExpandPatternCommand::ExpandPatternCommand( + std::weak_ptr mod, int songNum, int beginTrack, int beginColumn, + int beginOrder, int beginStep, int endTrack, int endColumn, int endStep) : AbstractCommand(CommandId::ExpandPattern), mod_(mod), song_(songNum), @@ -57,74 +56,40 @@ switch (c) { case 0: { - int n = (i % 2) ? -1 : std::stoi(prevCells_.at(i / 2).at(j)); - st.setNoteNumber(n); + if (i % 2) st.clearNoteNumber(); + else st.setNoteNumber(std::stoi(prevCells_.at(i / 2).at(j))); break; } case 1: { - int n = (i % 2) ? -1 : std::stoi(prevCells_.at(i / 2).at(j)); - st.setInstrumentNumber(n); + if (i % 2) st.clearInstrumentNumber(); + else st.setInstrumentNumber(std::stoi(prevCells_.at(i / 2).at(j))); break; } case 2: { - int v = (i % 2) ? -1 : std::stoi(prevCells_.at(i / 2).at(j)); - st.setVolume(v); + if (i % 2) st.clearVolume(); + else st.setVolume(std::stoi(prevCells_.at(i / 2).at(j))); break; } - case 3: + default: { - std::string id = (i % 2) ? "--" : prevCells_.at(i / 2).at(j); - st.setEffectID(0, id); - break; - } - case 4: - { - int v = (i % 2) ? -1 : std::stoi(prevCells_.at(i / 2).at(j)); - st.setEffectValue(0, v); - break; - } - case 5: - { - std::string id = (i % 2) ? "--" : prevCells_.at(i / 2).at(j); - st.setEffectID(1, id); - break; - } - case 6: - { - int v = (i % 2) ? -1 : std::stoi(prevCells_.at(i / 2).at(j)); - st.setEffectValue(1, v); - break; - } - case 7: - { - std::string id = (i % 2) ? "--" : prevCells_.at(i / 2).at(j); - st.setEffectID(2, id); - break; - } - case 8: - { - int v = (i % 2) ? -1 : std::stoi(prevCells_.at(i / 2).at(j)); - st.setEffectValue(2, v); - break; - } - case 9: - { - std::string id = (i % 2) ? "--" : prevCells_.at(i / 2).at(j); - st.setEffectID(3, id); - break; - } - case 10: - { - int v = (i % 2) ? -1 : std::stoi(prevCells_.at(i / 2).at(j)); - st.setEffectValue(3, v); + int ec = c - 3; + int ei = ec / 2; + if (ec % 2) { + if (i % 2) st.clearEffectValue(ei); + else st.setEffectValue(ei, std::stoi(prevCells_.at(i / 2).at(j))); + } + else { + if (i % 2) st.clearEffectId(ei); + else st.setEffectId(ei, prevCells_.at(i / 2).at(j)); + } break; } } - t += (++c / 11); - c %= 11; + t += (++c / Step::N_COLUMN); + c %= Step::N_COLUMN; } ++s; diff -Nru bambootracker-0.4.5+git20121209/BambooTracker/command/pattern/expand_pattern_command.hpp bambootracker-0.4.6/BambooTracker/command/pattern/expand_pattern_command.hpp --- bambootracker-0.4.5+git20121209/BambooTracker/command/pattern/expand_pattern_command.hpp 2020-12-08 14:05:28.000000000 +0000 +++ bambootracker-0.4.6/BambooTracker/command/pattern/expand_pattern_command.hpp 2021-02-11 14:37:17.000000000 +0000 @@ -31,7 +31,7 @@ #include "../abstract_command.hpp" #include "module.hpp" -class ExpandPatternCommand : public AbstractCommand +class ExpandPatternCommand final : public AbstractCommand { public: ExpandPatternCommand(std::weak_ptr mod, int songNum, diff -Nru bambootracker-0.4.5+git20121209/BambooTracker/command/pattern/insert_step_command.cpp bambootracker-0.4.6/BambooTracker/command/pattern/insert_step_command.cpp --- bambootracker-0.4.5+git20121209/BambooTracker/command/pattern/insert_step_command.cpp 2020-12-08 14:05:28.000000000 +0000 +++ bambootracker-0.4.6/BambooTracker/command/pattern/insert_step_command.cpp 2021-02-11 14:37:17.000000000 +0000 @@ -26,8 +26,8 @@ #include "insert_step_command.hpp" #include "pattern_command_utils.hpp" -InsertStepCommand::InsertStepCommand(std::weak_ptr mod, int songNum, int trackNum, - int orderNum, int stepNum) +InsertStepCommand::InsertStepCommand( + std::weak_ptr mod, int songNum, int trackNum, int orderNum, int stepNum) : AbstractCommand(CommandId::InsertStep), mod_(mod), song_(songNum), diff -Nru bambootracker-0.4.5+git20121209/BambooTracker/command/pattern/insert_step_command.hpp bambootracker-0.4.6/BambooTracker/command/pattern/insert_step_command.hpp --- bambootracker-0.4.5+git20121209/BambooTracker/command/pattern/insert_step_command.hpp 2020-12-08 14:05:28.000000000 +0000 +++ bambootracker-0.4.6/BambooTracker/command/pattern/insert_step_command.hpp 2021-02-11 14:37:17.000000000 +0000 @@ -29,7 +29,7 @@ #include "../abstract_command.hpp" #include "module.hpp" -class InsertStepCommand : public AbstractCommand +class InsertStepCommand final : public AbstractCommand { public: InsertStepCommand(std::weak_ptr mod, int songNum, int trackNum, int orderNum, int stepNum); diff -Nru bambootracker-0.4.5+git20121209/BambooTracker/command/pattern/interpolate_pattern_command.cpp bambootracker-0.4.6/BambooTracker/command/pattern/interpolate_pattern_command.cpp --- bambootracker-0.4.5+git20121209/BambooTracker/command/pattern/interpolate_pattern_command.cpp 2020-12-08 14:05:28.000000000 +0000 +++ bambootracker-0.4.6/BambooTracker/command/pattern/interpolate_pattern_command.cpp 2021-02-11 14:37:17.000000000 +0000 @@ -34,10 +34,9 @@ } } -InterpolatePatternCommand::InterpolatePatternCommand(std::weak_ptr mod, - int songNum, int beginTrack, int beginColumn, - int beginOrder, int beginStep, - int endTrack, int endColumn, int endStep) +InterpolatePatternCommand::InterpolatePatternCommand( + std::weak_ptr mod, int songNum, int beginTrack, int beginColumn, + int beginOrder, int beginStep, int endTrack, int endColumn, int endStep) : AbstractCommand(CommandId::InterpolatePattern), mod_(mod), song_(songNum), @@ -64,104 +63,43 @@ for (size_t i = 0; i < prevCells_.front().size(); ++i) { int s = bStep_; for (size_t j = 0; j < prevCells_.size(); ++j) { + Pattern& pattern = command_utils::getPattern(sng, t, order_); + Step& sa = pattern.getStep(bStep_); + Step& sb = pattern.getStep(eStep_); switch (c) { case 0: { - auto& pattern = command_utils::getPattern(sng, t, order_); - int a = pattern.getStep(bStep_).getNoteNumber(); - int b = pattern.getStep(eStep_).getNoteNumber(); - if (a > -1 && b > -1) - pattern.getStep(s).setNoteNumber(interp(a, b, j, div)); + if (sa.hasGeneralNote() && sb.hasGeneralNote()) + pattern.getStep(s).setNoteNumber( + interp(sa.getNoteNumber(), sb.getNoteNumber(), j, div)); break; } case 1: { - auto& pattern = command_utils::getPattern(sng, t, order_); - int a = pattern.getStep(bStep_).getInstrumentNumber(); - int b = pattern.getStep(eStep_).getInstrumentNumber(); - if (a > -1 && b > -1) - pattern.getStep(s).setInstrumentNumber(interp(a, b, j, div)); + if (sa.hasInstrument() && sb.hasInstrument()) + pattern.getStep(s).setInstrumentNumber(interp(sa.getInstrumentNumber(), sb.getInstrumentNumber(), j, div)); break; } case 2: { - auto& pattern = command_utils::getPattern(sng, t, order_); - int a = pattern.getStep(bStep_).getVolume(); - int b = pattern.getStep(eStep_).getVolume(); - if (a > -1 && b > -1) - pattern.getStep(s).setVolume(interp(a, b, j, div)); + if (sa.hasVolume() && sb.hasVolume()) + pattern.getStep(s).setVolume(interp(sa.getVolume(), sb.getVolume(), j, div)); break; } - case 3: + default: { - auto& pattern = command_utils::getPattern(sng, t, order_); - std::string a = pattern.getStep(bStep_).getEffectID(0); - std::string b = pattern.getStep(eStep_).getEffectID(0); - if (a == b) - pattern.getStep(s).setEffectID(0, a); - break; - } - case 4: - { - auto& pattern = command_utils::getPattern(sng, t, order_); - int a = pattern.getStep(bStep_).getEffectValue(0); - int b = pattern.getStep(eStep_).getEffectValue(0); - if (a > -1 && b > -1) - pattern.getStep(s).setEffectValue(0, interp(a, b, j, div)); - break; - } - case 5: - { - auto& pattern = command_utils::getPattern(sng, t, order_); - std::string a = pattern.getStep(bStep_).getEffectID(1); - std::string b = pattern.getStep(eStep_).getEffectID(1); - if (a == b) - pattern.getStep(s).setEffectID(1, a); - break; - } - case 6: - { - auto& pattern = command_utils::getPattern(sng, t, order_); - int a = pattern.getStep(bStep_).getEffectValue(1); - int b = pattern.getStep(eStep_).getEffectValue(1); - if (a > -1 && b > -1) - pattern.getStep(s).setEffectValue(1, interp(a, b, j, div)); - break; - } - case 7: - { - auto& pattern = command_utils::getPattern(sng, t, order_); - std::string a = pattern.getStep(bStep_).getEffectID(2); - std::string b = pattern.getStep(eStep_).getEffectID(2); - if (a == b) - pattern.getStep(s).setEffectID(2, a); - break; - } - case 8: - { - auto& pattern = command_utils::getPattern(sng, t, order_); - int a = pattern.getStep(bStep_).getEffectValue(2); - int b = pattern.getStep(eStep_).getEffectValue(2); - if (a > -1 && b > -1) - pattern.getStep(s).setEffectValue(2, interp(a, b, j, div)); - break; - } - case 9: - { - auto& pattern = command_utils::getPattern(sng, t, order_); - std::string a = pattern.getStep(bStep_).getEffectID(3); - std::string b = pattern.getStep(eStep_).getEffectID(3); - if (a == b) - pattern.getStep(s).setEffectID(3, a); - break; - } - case 10: - { - auto& pattern = command_utils::getPattern(sng, t, order_); - int a = pattern.getStep(bStep_).getEffectValue(3); - int b = pattern.getStep(eStep_).getEffectValue(3); - if (a > -1 && b > -1) - pattern.getStep(s).setEffectValue(3, interp(a, b, j, div)); + int ec = c - 3; + int ei = ec / 2; + if (ec % 2) { // Value + if (sa.hasEffectValue(ei) && sb.hasEffectValue(ei)) + pattern.getStep(s).setEffectValue(ei, interp(sa.getEffectValue(ei), sb.getEffectValue(ei), j, div)); + } + else { // ID + std::string a = sa.getEffectId(ei); + std::string b = sb.getEffectId(ei); + if (a == b) + pattern.getStep(s).setEffectId(ei, a); + } break; } } @@ -169,8 +107,8 @@ ++s; } ++c; - t += (c / 11); - c %= 11; + t += (c / Step::N_COLUMN); + c %= Step::N_COLUMN; } } diff -Nru bambootracker-0.4.5+git20121209/BambooTracker/command/pattern/interpolate_pattern_command.hpp bambootracker-0.4.6/BambooTracker/command/pattern/interpolate_pattern_command.hpp --- bambootracker-0.4.5+git20121209/BambooTracker/command/pattern/interpolate_pattern_command.hpp 2020-12-08 14:05:28.000000000 +0000 +++ bambootracker-0.4.6/BambooTracker/command/pattern/interpolate_pattern_command.hpp 2021-02-11 14:37:17.000000000 +0000 @@ -31,7 +31,7 @@ #include "../abstract_command.hpp" #include "module.hpp" -class InterpolatePatternCommand : public AbstractCommand +class InterpolatePatternCommand final : public AbstractCommand { public: InterpolatePatternCommand(std::weak_ptr mod, int songNum, diff -Nru bambootracker-0.4.5+git20121209/BambooTracker/command/pattern/paste_copied_data_to_pattern_command.cpp bambootracker-0.4.6/BambooTracker/command/pattern/paste_copied_data_to_pattern_command.cpp --- bambootracker-0.4.5+git20121209/BambooTracker/command/pattern/paste_copied_data_to_pattern_command.cpp 2020-12-08 14:05:28.000000000 +0000 +++ bambootracker-0.4.6/BambooTracker/command/pattern/paste_copied_data_to_pattern_command.cpp 2021-02-11 14:37:17.000000000 +0000 @@ -26,10 +26,9 @@ #include "paste_copied_data_to_pattern_command.hpp" #include "pattern_command_utils.hpp" -PasteCopiedDataToPatternCommand::PasteCopiedDataToPatternCommand(std::weak_ptr mod, int songNum, - int beginTrack, int beginColmn, - int beginOrder, int beginStep, - std::vector> cells) +PasteCopiedDataToPatternCommand::PasteCopiedDataToPatternCommand( + std::weak_ptr mod, int songNum, int beginTrack, int beginColmn, + int beginOrder, int beginStep, const std::vector>& cells) : AbstractCommand(CommandId::PasteCopiedDataToPattern), mod_(mod), song_(songNum), diff -Nru bambootracker-0.4.5+git20121209/BambooTracker/command/pattern/paste_copied_data_to_pattern_command.hpp bambootracker-0.4.6/BambooTracker/command/pattern/paste_copied_data_to_pattern_command.hpp --- bambootracker-0.4.5+git20121209/BambooTracker/command/pattern/paste_copied_data_to_pattern_command.hpp 2020-12-08 14:05:28.000000000 +0000 +++ bambootracker-0.4.6/BambooTracker/command/pattern/paste_copied_data_to_pattern_command.hpp 2021-02-11 14:37:17.000000000 +0000 @@ -31,11 +31,12 @@ #include "../abstract_command.hpp" #include "module.hpp" -class PasteCopiedDataToPatternCommand : public AbstractCommand +class PasteCopiedDataToPatternCommand final : public AbstractCommand { public: - PasteCopiedDataToPatternCommand(std::weak_ptr mod, int songNum, int beginTrack, int beginColmn, - int beginOrder, int beginStep, std::vector> cells); + PasteCopiedDataToPatternCommand( + std::weak_ptr mod, int songNum, int beginTrack, int beginColmn, + int beginOrder, int beginStep, const std::vector>& cells); void redo() override; void undo() override; diff -Nru bambootracker-0.4.5+git20121209/BambooTracker/command/pattern/paste_insert_copied_data_to_pattern_command.cpp bambootracker-0.4.6/BambooTracker/command/pattern/paste_insert_copied_data_to_pattern_command.cpp --- bambootracker-0.4.5+git20121209/BambooTracker/command/pattern/paste_insert_copied_data_to_pattern_command.cpp 2020-12-08 14:05:28.000000000 +0000 +++ bambootracker-0.4.6/BambooTracker/command/pattern/paste_insert_copied_data_to_pattern_command.cpp 2021-02-11 14:37:17.000000000 +0000 @@ -29,7 +29,7 @@ PasteInsertCopiedDataToPatternCommand::PasteInsertCopiedDataToPatternCommand( std::weak_ptr mod, int songNum, int beginTrack, int beginColumn, - int beginOrder, int beginStep, std::vector> cells) + int beginOrder, int beginStep, const std::vector>& cells) : AbstractCommand(CommandId::PasteInsertCopiedDataToPattern), mod_(mod), song_(songNum), diff -Nru bambootracker-0.4.5+git20121209/BambooTracker/command/pattern/paste_insert_copied_data_to_pattern_command.hpp bambootracker-0.4.6/BambooTracker/command/pattern/paste_insert_copied_data_to_pattern_command.hpp --- bambootracker-0.4.5+git20121209/BambooTracker/command/pattern/paste_insert_copied_data_to_pattern_command.hpp 2020-12-08 14:05:28.000000000 +0000 +++ bambootracker-0.4.6/BambooTracker/command/pattern/paste_insert_copied_data_to_pattern_command.hpp 2021-02-11 14:37:17.000000000 +0000 @@ -31,11 +31,12 @@ #include "../abstract_command.hpp" #include "module.hpp" -class PasteInsertCopiedDataToPatternCommand : public AbstractCommand +class PasteInsertCopiedDataToPatternCommand final : public AbstractCommand { public: - PasteInsertCopiedDataToPatternCommand(std::weak_ptr mod, int songNum, int beginTrack, int beginColumn, - int beginOrder, int beginStep, std::vector> cells); + PasteInsertCopiedDataToPatternCommand( + std::weak_ptr mod, int songNum, int beginTrack, int beginColumn, + int beginOrder, int beginStep, const std::vector>& cells); void redo() override; void undo() override; diff -Nru bambootracker-0.4.5+git20121209/BambooTracker/command/pattern/paste_mix_copied_data_to_pattern_command.cpp bambootracker-0.4.6/BambooTracker/command/pattern/paste_mix_copied_data_to_pattern_command.cpp --- bambootracker-0.4.5+git20121209/BambooTracker/command/pattern/paste_mix_copied_data_to_pattern_command.cpp 2020-12-08 14:05:28.000000000 +0000 +++ bambootracker-0.4.6/BambooTracker/command/pattern/paste_mix_copied_data_to_pattern_command.cpp 2021-02-11 14:37:17.000000000 +0000 @@ -26,10 +26,9 @@ #include "paste_mix_copied_data_to_pattern_command.hpp" #include "pattern_command_utils.hpp" -PasteMixCopiedDataToPatternCommand::PasteMixCopiedDataToPatternCommand(std::weak_ptr mod, int songNum, - int beginTrack, int beginColumn, - int beginOrder, int beginStep, - std::vector> cells) +PasteMixCopiedDataToPatternCommand::PasteMixCopiedDataToPatternCommand( + std::weak_ptr mod, int songNum, int beginTrack, int beginColumn, + int beginOrder, int beginStep, const std::vector>& cells) : AbstractCommand(CommandId::PasteMixCopiedDataToPattern), mod_(mod), song_(songNum), @@ -58,70 +57,39 @@ case 0: { int n = std::stoi(cell); - if (n != -1 && step.getNoteNumber() == -1) step.setNoteNumber(n); + if (!Step::testEmptyNote(n) && step.isEmptyNote()) step.setNoteNumber(n); break; } case 1: { int n = std::stoi(cell); - if (n != -1 && step.getInstrumentNumber() == -1) step.setInstrumentNumber(n); + if (!Step::testEmptyInstrument(n) && !step.hasInstrument()) step.setInstrumentNumber(n); break; } case 2: { int vol = std::stoi(cell); - if (vol != -1 && step.getVolume() == -1) step.setVolume(vol); + if (!Step::testEmptyVolume(vol) && !step.hasVolume()) step.setVolume(vol); break; } - case 3: + default: { - if (cell != "--" && step.getEffectID(0) == "--") step.setEffectID(0, cell); - break; - } - case 4: - { - int val = std::stoi(cell); - if (val != -1 && step.getEffectValue(0) == -1) step.setEffectValue(0, val); - break; - } - case 5: - { - if (cell != "--" && step.getEffectID(1) == "--") step.setEffectID(1, cell); - break; - } - case 6: - { - int val = std::stoi(cell); - if (val != -1 && step.getEffectValue(1) == -1) step.setEffectValue(1, val); - break; - } - case 7: - { - if (cell!= "--" && step.getEffectID(2) == "--") step.setEffectID(2, cell); - break; - } - case 8: - { - int val = std::stoi(cell); - if (val != -1 && step.getEffectValue(2) == -1) step.setEffectValue(2, val); - break; - } - case 9: - { - if (cell != "--" && step.getEffectID(3) == "--") step.setEffectID(3, cell); - break; - } - case 10: - { - int val = std::stoi(cell); - if (val != -1 && step.getEffectValue(3) == -1) step.setEffectValue(3, val); + int ec = c - 3; + int ei = ec / 2; + if (ec % 2) { // Value + int val = std::stoi(cell); + if (!Step::testEmptyEffectValue(val) && !step.hasEffectValue(ei)) step.setEffectValue(ei, val); + } + else { // ID + if (!Step::testEmptyEffectId(cell) && !step.hasEffectId(ei)) step.setEffectId(ei, cell); + } break; } } ++c; - t += (c / 11); - c %= 11; + t += (c / Step::N_COLUMN); + c %= Step::N_COLUMN; } ++s; diff -Nru bambootracker-0.4.5+git20121209/BambooTracker/command/pattern/paste_mix_copied_data_to_pattern_command.hpp bambootracker-0.4.6/BambooTracker/command/pattern/paste_mix_copied_data_to_pattern_command.hpp --- bambootracker-0.4.5+git20121209/BambooTracker/command/pattern/paste_mix_copied_data_to_pattern_command.hpp 2020-12-08 14:05:28.000000000 +0000 +++ bambootracker-0.4.6/BambooTracker/command/pattern/paste_mix_copied_data_to_pattern_command.hpp 2021-02-11 14:37:17.000000000 +0000 @@ -31,11 +31,12 @@ #include "../abstract_command.hpp" #include "module.hpp" -class PasteMixCopiedDataToPatternCommand : public AbstractCommand +class PasteMixCopiedDataToPatternCommand final : public AbstractCommand { public: - PasteMixCopiedDataToPatternCommand(std::weak_ptr mod, int songNum, int beginTrack, int beginColumn, - int beginOrder, int beginStep, std::vector> cells); + PasteMixCopiedDataToPatternCommand( + std::weak_ptr mod, int songNum, int beginTrack, int beginColumn, + int beginOrder, int beginStep, const std::vector>& cells); void redo() override; void undo() override; diff -Nru bambootracker-0.4.5+git20121209/BambooTracker/command/pattern/paste_overwrite_copied_data_to_pattern_command.cpp bambootracker-0.4.6/BambooTracker/command/pattern/paste_overwrite_copied_data_to_pattern_command.cpp --- bambootracker-0.4.5+git20121209/BambooTracker/command/pattern/paste_overwrite_copied_data_to_pattern_command.cpp 2020-12-08 14:05:28.000000000 +0000 +++ bambootracker-0.4.6/BambooTracker/command/pattern/paste_overwrite_copied_data_to_pattern_command.cpp 2021-02-11 14:37:17.000000000 +0000 @@ -28,7 +28,7 @@ PasteOverwriteCopiedDataToPatternCommand::PasteOverwriteCopiedDataToPatternCommand( std::weak_ptr mod, int songNum, int beginTrack, int beginColumn, - int beginOrder, int beginStep, std::vector> cells) + int beginOrder, int beginStep, const std::vector>& cells) : AbstractCommand(CommandId::PasteOverwriteCopiedDataToPattern), mod_(mod), song_(songNum), @@ -57,69 +57,38 @@ case 0: { int n = std::stoi(cell); - if (n != -1) st.setNoteNumber(n); + if (!Step::testEmptyNote(n)) st.setNoteNumber(n); break; } case 1: { int n = std::stoi(cell); - if (n != -1) st.setInstrumentNumber(n); + if (!Step::testEmptyInstrument(n)) st.setInstrumentNumber(n); break; } case 2: { int vol = std::stoi(cell); - if (vol != -1) st.setVolume(vol); + if (!Step::testEmptyVolume(vol)) st.setVolume(vol); break; } - case 3: + default: { - if (cell != "--") st.setEffectID(0, cell); - break; - } - case 4: - { - int val = std::stoi(cell); - if (val != -1) st.setEffectValue(0, val); - break; - } - case 5: - { - if (cell != "--") st.setEffectID(1, cell); - break; - } - case 6: - { - int val = std::stoi(cell); - if (val != -1) st.setEffectValue(1, val); - break; - } - case 7: - { - if (cell != "--") st.setEffectID(2, cell); - break; - } - case 8: - { - int val = std::stoi(cell); - if (val != -1) st.setEffectValue(2, val); - break; - } - case 9: - { - if (cell != "--") st.setEffectID(3, cell); - break; - } - case 10: - { - int val = std::stoi(cell); - if (val != -1) st.setEffectValue(3, val); + int ec = c - 3; + int ei = ec / 2; + if (ec % 2) { + int val = std::stoi(cell); + if (!Step::testEmptyEffectValue(val)) st.setEffectValue(ei, val); + } + else { + if (!Step::testEmptyEffectId(cell)) st.setEffectId(ei, cell); + } break; } } - t += (++c / 11); - c %= 11; + t += (++c / Step::N_COLUMN); + c %= Step::N_COLUMN; } ++s; diff -Nru bambootracker-0.4.5+git20121209/BambooTracker/command/pattern/paste_overwrite_copied_data_to_pattern_command.hpp bambootracker-0.4.6/BambooTracker/command/pattern/paste_overwrite_copied_data_to_pattern_command.hpp --- bambootracker-0.4.5+git20121209/BambooTracker/command/pattern/paste_overwrite_copied_data_to_pattern_command.hpp 2020-12-08 14:05:28.000000000 +0000 +++ bambootracker-0.4.6/BambooTracker/command/pattern/paste_overwrite_copied_data_to_pattern_command.hpp 2021-02-11 14:37:17.000000000 +0000 @@ -31,12 +31,12 @@ #include "../abstract_command.hpp" #include "module.hpp" -class PasteOverwriteCopiedDataToPatternCommand : public AbstractCommand +class PasteOverwriteCopiedDataToPatternCommand final : public AbstractCommand { public: - PasteOverwriteCopiedDataToPatternCommand(std::weak_ptr mod, int songNum, int beginTrack, int beginColumn, - int beginOrder, int beginStep, - std::vector> cells); + PasteOverwriteCopiedDataToPatternCommand( + std::weak_ptr mod, int songNum, int beginTrack, int beginColumn, + int beginOrder, int beginStep, const std::vector>& cells); void redo() override; void undo() override; diff -Nru bambootracker-0.4.5+git20121209/BambooTracker/command/pattern/pattern_command_utils.cpp bambootracker-0.4.6/BambooTracker/command/pattern/pattern_command_utils.cpp --- bambootracker-0.4.5+git20121209/BambooTracker/command/pattern/pattern_command_utils.cpp 2020-12-08 14:05:28.000000000 +0000 +++ bambootracker-0.4.6/BambooTracker/command/pattern/pattern_command_utils.cpp 2021-02-11 14:37:17.000000000 +0000 @@ -29,6 +29,7 @@ { size_t calculateColumnSize(int beginTrack, int beginColumn, int endTrack, int endColumn) { + constexpr int WCOL = Step::N_COLUMN - 1; int w = 0; int tr = endTrack; int cl = endColumn; @@ -39,7 +40,7 @@ } else { w += (cl + 1); - cl = 10; + cl = WCOL; --tr; } } @@ -63,18 +64,22 @@ case 0: val = std::to_string(st.getNoteNumber()); break; case 1: val = std::to_string(st.getInstrumentNumber()); break; case 2: val = std::to_string(st.getVolume()); break; - case 3: val = st.getEffectID(0); break; - case 4: val = std::to_string(st.getEffectValue(0)); break; - case 5: val = st.getEffectID(1); break; - case 6: val = std::to_string(st.getEffectValue(1)); break; - case 7: val = st.getEffectID(2); break; - case 8: val = std::to_string(st.getEffectValue(2)); break; - case 9: val = st.getEffectID(3); break; - case 10: val = std::to_string(st.getEffectValue(3)); break; + default: + { + int ec = c - 3; + int ei = ec / 2; + if (ec % 2) { // Value + val = std::to_string(st.getEffectValue(ei)); + } + else { // ID + val = st.getEffectId(ei); + } + break; + } } cells[i][j] = val; - t += (++c / 11); + t += (++c / Step::N_COLUMN); c %= 11; } ++s; @@ -95,18 +100,22 @@ case 0: st.setNoteNumber(std::stoi(cell)); break; case 1: st.setInstrumentNumber(std::stoi(cell)); break; case 2: st.setVolume(std::stoi(cell)); break; - case 3: st.setEffectID(0, cell); break; - case 4: st.setEffectValue(0, std::stoi(cell)); break; - case 5: st.setEffectID(1, cell); break; - case 6: st.setEffectValue(1, std::stoi(cell)); break; - case 7: st.setEffectID(2, cell); break; - case 8: st.setEffectValue(2, std::stoi(cell)); break; - case 9: st.setEffectID(3, cell); break; - case 10: st.setEffectValue(3, std::stoi(cell)); break; + default: + { + int ec = c - 3; + int ei = ec / 2; + if (ec % 2) { // Value + st.setEffectValue(ei, std::stoi(cell)); + } + else { // ID + st.setEffectId(ei, cell); + } + break; + } } - t += (++c / 11); - c %= 11; + t += (++c / Step::N_COLUMN); + c %= Step::N_COLUMN; } ++s; } diff -Nru bambootracker-0.4.5+git20121209/BambooTracker/command/pattern/replace_instrument_in_pattern_command.cpp bambootracker-0.4.6/BambooTracker/command/pattern/replace_instrument_in_pattern_command.cpp --- bambootracker-0.4.5+git20121209/BambooTracker/command/pattern/replace_instrument_in_pattern_command.cpp 2020-12-08 14:05:28.000000000 +0000 +++ bambootracker-0.4.6/BambooTracker/command/pattern/replace_instrument_in_pattern_command.cpp 2021-02-11 14:37:17.000000000 +0000 @@ -26,10 +26,9 @@ #include "replace_instrument_in_pattern_command.hpp" #include "pattern_command_utils.hpp" -ReplaceInstrumentInPatternCommand::ReplaceInstrumentInPatternCommand(std::weak_ptr mod, - int songNum, int beginTrack, - int beginOrder, int beginStep, - int endTrack, int endStep, int newInst) +ReplaceInstrumentInPatternCommand::ReplaceInstrumentInPatternCommand( + std::weak_ptr mod, int songNum, int beginTrack, + int beginOrder, int beginStep, int endTrack, int endStep, int newInst) : AbstractCommand(CommandId::ReplaceInstrumentInPattern), mod_(mod), song_(songNum), @@ -44,8 +43,8 @@ for (int step = beginStep; step <= endStep; ++step) { for (int track = beginTrack; track <= endTrack; ++track) { - int n = command_utils::getStep(sng, track, beginOrder, step).getInstrumentNumber(); - if (n > -1) prevInsts_.push_back(n); + Step& st = command_utils::getStep(sng, track, beginOrder, step); + if (st.hasInstrument()) prevInsts_.push_back(st.getInstrumentNumber()); } } } @@ -56,9 +55,8 @@ for (int step = bStep_; step <= eStep_; ++step) { for (int track = bTrack_; track <= eTrack_; ++track) { - Step& s = command_utils::getStep(sng, track, order_, step); - int n = s.getInstrumentNumber(); - if (n > -1) s.setInstrumentNumber(inst_); + Step& st = command_utils::getStep(sng, track, order_, step); + if (st.hasInstrument()) st.setInstrumentNumber(inst_); } } } @@ -70,8 +68,8 @@ size_t i = 0; for (int step = bStep_; step <= eStep_; ++step) { for (int track = bTrack_; track <= eTrack_; ++track) { - Step& s = command_utils::getStep(sng, track, order_, step); - if (s.getInstrumentNumber() > -1) s.setInstrumentNumber(prevInsts_.at(i)); + Step& st = command_utils::getStep(sng, track, order_, step); + if (st.hasInstrument()) st.setInstrumentNumber(prevInsts_.at(i)); } } } diff -Nru bambootracker-0.4.5+git20121209/BambooTracker/command/pattern/replace_instrument_in_pattern_command.hpp bambootracker-0.4.6/BambooTracker/command/pattern/replace_instrument_in_pattern_command.hpp --- bambootracker-0.4.5+git20121209/BambooTracker/command/pattern/replace_instrument_in_pattern_command.hpp 2020-12-08 14:05:28.000000000 +0000 +++ bambootracker-0.4.6/BambooTracker/command/pattern/replace_instrument_in_pattern_command.hpp 2021-02-11 14:37:17.000000000 +0000 @@ -30,7 +30,7 @@ #include "../abstract_command.hpp" #include "module.hpp" -class ReplaceInstrumentInPatternCommand : public AbstractCommand +class ReplaceInstrumentInPatternCommand final : public AbstractCommand { public: ReplaceInstrumentInPatternCommand(std::weak_ptr mod, int songNum, diff -Nru bambootracker-0.4.5+git20121209/BambooTracker/command/pattern/reverse_pattern_command.cpp bambootracker-0.4.6/BambooTracker/command/pattern/reverse_pattern_command.cpp --- bambootracker-0.4.5+git20121209/BambooTracker/command/pattern/reverse_pattern_command.cpp 2020-12-08 14:05:28.000000000 +0000 +++ bambootracker-0.4.6/BambooTracker/command/pattern/reverse_pattern_command.cpp 2021-02-11 14:37:17.000000000 +0000 @@ -26,10 +26,9 @@ #include "reverse_pattern_command.hpp" #include "pattern_command_utils.hpp" -ReversePatternCommand::ReversePatternCommand(std::weak_ptr mod, - int songNum, int beginTrack, int beginColumn, - int beginOrder, int beginStep, - int endTrack, int endColumn, int endStep) +ReversePatternCommand::ReversePatternCommand( + std::weak_ptr mod, int songNum, int beginTrack, int beginColumn, + int beginOrder, int beginStep, int endTrack, int endColumn, int endStep) : AbstractCommand(CommandId::ReversePattern), mod_(mod), song_(songNum), @@ -59,18 +58,18 @@ case 0: st.setNoteNumber(std::stoi(prevCells_.at(l - i).at(j))); break; case 1: st.setInstrumentNumber(std::stoi(prevCells_.at(l - i).at(j))); break; case 2: st.setVolume(std::stoi(prevCells_.at(l - i).at(j))); break; - case 3: st.setEffectID(0, prevCells_.at(l - i).at(j)); break; - case 4: st.setEffectValue(0, std::stoi(prevCells_.at(l - i).at(j))); break; - case 5: st.setEffectID(1, prevCells_.at(l - i).at(j)); break; - case 6: st.setEffectValue(1, std::stoi(prevCells_.at(l - i).at(j))); break; - case 7: st.setEffectID(2, prevCells_.at(l - i).at(j)); break; - case 8: st.setEffectValue(2, std::stoi(prevCells_.at(l - i).at(j))); break; - case 9: st.setEffectID(3, prevCells_.at(l - i).at(j)); break; - case 10: st.setEffectValue(3, std::stoi(prevCells_.at(l - i).at(j))); break; + default: + { + int ec = c - 3; + int ei = ec / 2; + if (ec % 2) st.setEffectValue(ei, std::stoi(prevCells_.at(l - i).at(j))); + else st.setEffectId(ei, prevCells_.at(l - i).at(j)); + break; + } } - t += (++c / 11); - c %= 11; + t += (++c / Step::N_COLUMN); + c %= Step::N_COLUMN; } ++s; diff -Nru bambootracker-0.4.5+git20121209/BambooTracker/command/pattern/reverse_pattern_command.hpp bambootracker-0.4.6/BambooTracker/command/pattern/reverse_pattern_command.hpp --- bambootracker-0.4.5+git20121209/BambooTracker/command/pattern/reverse_pattern_command.hpp 2020-12-08 14:05:28.000000000 +0000 +++ bambootracker-0.4.6/BambooTracker/command/pattern/reverse_pattern_command.hpp 2021-02-11 14:37:17.000000000 +0000 @@ -31,7 +31,7 @@ #include "../abstract_command.hpp" #include "module.hpp" -class ReversePatternCommand : public AbstractCommand +class ReversePatternCommand final : public AbstractCommand { public: ReversePatternCommand(std::weak_ptr mod, int songNum, diff -Nru bambootracker-0.4.5+git20121209/BambooTracker/command/pattern/set_echo_buffer_access_command.cpp bambootracker-0.4.6/BambooTracker/command/pattern/set_echo_buffer_access_command.cpp --- bambootracker-0.4.5+git20121209/BambooTracker/command/pattern/set_echo_buffer_access_command.cpp 2020-12-08 14:05:28.000000000 +0000 +++ bambootracker-0.4.6/BambooTracker/command/pattern/set_echo_buffer_access_command.cpp 2021-02-11 14:37:17.000000000 +0000 @@ -41,7 +41,7 @@ void SetEchoBufferAccessCommand::redo() { - command_utils::getStep(mod_, song_, track_, order_, step_).setNoteNumber(-buf_ - 3); + command_utils::getStep(mod_, song_, track_, order_, step_).setEchoBuffer(buf_); } void SetEchoBufferAccessCommand::undo() diff -Nru bambootracker-0.4.5+git20121209/BambooTracker/command/pattern/set_echo_buffer_access_command.hpp bambootracker-0.4.6/BambooTracker/command/pattern/set_echo_buffer_access_command.hpp --- bambootracker-0.4.5+git20121209/BambooTracker/command/pattern/set_echo_buffer_access_command.hpp 2020-12-08 14:05:28.000000000 +0000 +++ bambootracker-0.4.6/BambooTracker/command/pattern/set_echo_buffer_access_command.hpp 2021-02-11 14:37:17.000000000 +0000 @@ -30,7 +30,7 @@ #include "../abstract_command.hpp" #include "module.hpp" -class SetEchoBufferAccessCommand : public AbstractCommand +class SetEchoBufferAccessCommand final : public AbstractCommand { public: SetEchoBufferAccessCommand(std::weak_ptr mod, int songNum, int trackNum, diff -Nru bambootracker-0.4.5+git20121209/BambooTracker/command/pattern/set_effect_id_to_step_command.cpp bambootracker-0.4.6/BambooTracker/command/pattern/set_effect_id_to_step_command.cpp --- bambootracker-0.4.5+git20121209/BambooTracker/command/pattern/set_effect_id_to_step_command.cpp 2020-12-08 14:05:28.000000000 +0000 +++ bambootracker-0.4.6/BambooTracker/command/pattern/set_effect_id_to_step_command.cpp 2021-02-11 14:37:17.000000000 +0000 @@ -26,10 +26,9 @@ #include "set_effect_id_to_step_command.hpp" #include "pattern_command_utils.hpp" -SetEffectIDToStepCommand::SetEffectIDToStepCommand(std::weak_ptr mod, int songNum, - int trackNum, int orderNum, int stepNum, - int n, std::string id, bool fillValue00, - bool secondEntry) +SetEffectIDToStepCommand::SetEffectIDToStepCommand( + std::weak_ptr mod, int songNum, int trackNum, int orderNum, int stepNum, + int n, std::string id, bool fillValue00, bool secondEntry) : AbstractCommand(CommandId::SetEffectIDToStep), mod_(mod), song_(songNum), @@ -41,23 +40,23 @@ isSecondEntry_(secondEntry) { Step& step = command_utils::getStep(mod, songNum, trackNum, orderNum, stepNum); - prevEffID_ = step.getEffectID(n); - filledValue00_ = fillValue00 && (step.getEffectValue(n) == -1); + prevEffID_ = step.getEffectId(n); + filledValue00_ = fillValue00 && !step.hasEffectValue(n); } void SetEffectIDToStepCommand::redo() { std::string str = isSecondEntry_ ? effID_ : ("0" + effID_); Step& step = command_utils::getStep(mod_, song_, track_, order_, step_); - step.setEffectID(n_, str); + step.setEffectId(n_, str); if (filledValue00_) step.setEffectValue(n_, 0); } void SetEffectIDToStepCommand::undo() { Step& step = command_utils::getStep(mod_, song_, track_, order_, step_); - step.setEffectID(n_, prevEffID_); - if (filledValue00_) step.setEffectValue(n_, -1); + step.setEffectId(n_, prevEffID_); + if (filledValue00_) step.clearEffectValue(n_); if (!isSecondEntry_) { // Forced complete effID_ = "0" + effID_; diff -Nru bambootracker-0.4.5+git20121209/BambooTracker/command/pattern/set_effect_id_to_step_command.hpp bambootracker-0.4.6/BambooTracker/command/pattern/set_effect_id_to_step_command.hpp --- bambootracker-0.4.5+git20121209/BambooTracker/command/pattern/set_effect_id_to_step_command.hpp 2020-12-08 14:05:28.000000000 +0000 +++ bambootracker-0.4.6/BambooTracker/command/pattern/set_effect_id_to_step_command.hpp 2021-02-11 14:37:17.000000000 +0000 @@ -30,11 +30,12 @@ #include "../abstract_command.hpp" #include "module.hpp" -class SetEffectIDToStepCommand : public AbstractCommand +class SetEffectIDToStepCommand final : public AbstractCommand { public: - SetEffectIDToStepCommand(std::weak_ptr mod, int songNum, int trackNum, int orderNum, int stepNum, - int n, std::string id, bool fillValue00, bool secondEntry); + SetEffectIDToStepCommand( + std::weak_ptr mod, int songNum, int trackNum, int orderNum, int stepNum, + int n, std::string id, bool fillValue00, bool secondEntry); void redo() override; void undo() override; bool mergeWith(const AbstractCommand* other) override; diff -Nru bambootracker-0.4.5+git20121209/BambooTracker/command/pattern/set_effect_value_to_step_command.cpp bambootracker-0.4.6/BambooTracker/command/pattern/set_effect_value_to_step_command.cpp --- bambootracker-0.4.5+git20121209/BambooTracker/command/pattern/set_effect_value_to_step_command.cpp 2020-12-08 14:05:28.000000000 +0000 +++ bambootracker-0.4.6/BambooTracker/command/pattern/set_effect_value_to_step_command.cpp 2021-02-11 14:37:17.000000000 +0000 @@ -25,10 +25,11 @@ #include "set_effect_value_to_step_command.hpp" #include "pattern_command_utils.hpp" +#include "effect.hpp" -SetEffectValueToStepCommand::SetEffectValueToStepCommand(std::weak_ptr mod, int songNum, int trackNum, - int orderNum, int stepNum, int n, int value, - EffectDisplayControl ctrl, bool secondEntry) +SetEffectValueToStepCommand::SetEffectValueToStepCommand( + std::weak_ptr mod, int songNum, int trackNum, int orderNum, int stepNum, + int n, int value, EffectDisplayControl ctrl, bool secondEntry) : AbstractCommand(CommandId::SetEffectValueToStep), mod_(mod), song_(songNum), @@ -52,10 +53,10 @@ value = val_; break; case EffectDisplayControl::ReverseFMVolumeDelay: - value = (val_ < 0x80) ? (0x7f - val_) : val_; + value = effect_utils::reverseFmVolume(val_); break; case EffectDisplayControl::ReverseFMBrightness: - value = (val_ > 0) ? (0xff - val_ + 1) : val_; + value = effect_utils::reverseFmBrightness(val_); break; } command_utils::getStep(mod_, song_, track_, order_, step_).setEffectValue(n_, value); diff -Nru bambootracker-0.4.5+git20121209/BambooTracker/command/pattern/set_effect_value_to_step_command.hpp bambootracker-0.4.6/BambooTracker/command/pattern/set_effect_value_to_step_command.hpp --- bambootracker-0.4.5+git20121209/BambooTracker/command/pattern/set_effect_value_to_step_command.hpp 2020-12-08 14:05:28.000000000 +0000 +++ bambootracker-0.4.6/BambooTracker/command/pattern/set_effect_value_to_step_command.hpp 2021-02-11 14:37:17.000000000 +0000 @@ -28,9 +28,13 @@ #include #include "../abstract_command.hpp" #include "module.hpp" -#include "misc.hpp" -class SetEffectValueToStepCommand : public AbstractCommand +enum class EffectDisplayControl +{ + Unset, ReverseFMVolumeDelay, ReverseFMBrightness +}; + +class SetEffectValueToStepCommand final : public AbstractCommand { public: SetEffectValueToStepCommand(std::weak_ptr mod, int songNum, int trackNum, diff -Nru bambootracker-0.4.5+git20121209/BambooTracker/command/pattern/set_instrument_to_step_command.cpp bambootracker-0.4.6/BambooTracker/command/pattern/set_instrument_to_step_command.cpp --- bambootracker-0.4.5+git20121209/BambooTracker/command/pattern/set_instrument_to_step_command.cpp 2020-12-08 14:05:28.000000000 +0000 +++ bambootracker-0.4.6/BambooTracker/command/pattern/set_instrument_to_step_command.cpp 2021-02-11 14:37:17.000000000 +0000 @@ -26,7 +26,9 @@ #include "set_instrument_to_step_command.hpp" #include "pattern_command_utils.hpp" -SetInstrumentToStepCommand::SetInstrumentToStepCommand(std::weak_ptr mod, int songNum, int trackNum, int orderNum, int stepNum, int instNum, bool secondEntry) +SetInstrumentToStepCommand::SetInstrumentToStepCommand( + std::weak_ptr mod, int songNum, int trackNum, int orderNum, + int stepNum, int instNum, bool secondEntry) : AbstractCommand(CommandId::SetInstrumentInStep), mod_(mod), song_(songNum), diff -Nru bambootracker-0.4.5+git20121209/BambooTracker/command/pattern/set_instrument_to_step_command.hpp bambootracker-0.4.6/BambooTracker/command/pattern/set_instrument_to_step_command.hpp --- bambootracker-0.4.5+git20121209/BambooTracker/command/pattern/set_instrument_to_step_command.hpp 2020-12-08 14:05:28.000000000 +0000 +++ bambootracker-0.4.6/BambooTracker/command/pattern/set_instrument_to_step_command.hpp 2021-02-11 14:37:17.000000000 +0000 @@ -29,7 +29,7 @@ #include "../abstract_command.hpp" #include "module.hpp" -class SetInstrumentToStepCommand : public AbstractCommand +class SetInstrumentToStepCommand final : public AbstractCommand { public: SetInstrumentToStepCommand(std::weak_ptr mod, int songNum, int trackNum, diff -Nru bambootracker-0.4.5+git20121209/BambooTracker/command/pattern/set_key_off_to_step_command.cpp bambootracker-0.4.6/BambooTracker/command/pattern/set_key_off_to_step_command.cpp --- bambootracker-0.4.5+git20121209/BambooTracker/command/pattern/set_key_off_to_step_command.cpp 2020-12-08 14:05:28.000000000 +0000 +++ bambootracker-0.4.6/BambooTracker/command/pattern/set_key_off_to_step_command.cpp 2021-02-11 14:37:17.000000000 +0000 @@ -26,7 +26,8 @@ #include "set_key_off_to_step_command.hpp" #include "pattern_command_utils.hpp" -SetKeyOffToStepCommand::SetKeyOffToStepCommand(std::weak_ptr mod, int songNum, int trackNum, int orderNum, int stepNum) +SetKeyOffToStepCommand::SetKeyOffToStepCommand( + std::weak_ptr mod, int songNum, int trackNum, int orderNum, int stepNum) : AbstractCommand(CommandId::SetKeyOffToStep), mod_(mod), song_(songNum), @@ -38,21 +39,19 @@ prevNote_ = st.getNoteNumber(); prevInst_ = st.getInstrumentNumber(); prevVol_ = st.getVolume(); - for (int i = 0; i < 4; ++i) { - prevEffID_[i] = st.getEffectID(i); - prevEffVal_[i] = st.getEffectValue(i); + for (int i = 0; i < Step::N_EFFECT; ++i) { + prevEff_[i] = st.getEffect(i); } } void SetKeyOffToStepCommand::redo() { Step& st = command_utils::getStep(mod_, song_, track_, order_, step_); - st.setNoteNumber(-2); - st.setInstrumentNumber(-1); - st.setVolume(-1); - for (int i = 0; i < 4; ++i) { - st.setEffectID(i, "--"); - st.setEffectValue(i, -1); + st.setKeyOff(); + st.clearInstrumentNumber(); + st.clearVolume(); + for (int i = 0; i < Step::N_EFFECT; ++i) { + st.clearEffect(i); } } @@ -62,8 +61,7 @@ st.setNoteNumber(prevNote_); st.setInstrumentNumber(prevInst_); st.setVolume(prevVol_); - for (int i = 0; i < 4; ++i) { - st.setEffectID(i, prevEffID_[i]); - st.setEffectValue(i, prevEffVal_[i]); + for (int i = 0; i < Step::N_EFFECT; ++i) { + st.setEffect(i, prevEff_[i]); } } diff -Nru bambootracker-0.4.5+git20121209/BambooTracker/command/pattern/set_key_off_to_step_command.hpp bambootracker-0.4.6/BambooTracker/command/pattern/set_key_off_to_step_command.hpp --- bambootracker-0.4.5+git20121209/BambooTracker/command/pattern/set_key_off_to_step_command.hpp 2020-12-08 14:05:28.000000000 +0000 +++ bambootracker-0.4.6/BambooTracker/command/pattern/set_key_off_to_step_command.hpp 2021-02-11 14:37:17.000000000 +0000 @@ -30,7 +30,7 @@ #include "../abstract_command.hpp" #include "module.hpp" -class SetKeyOffToStepCommand : public AbstractCommand +class SetKeyOffToStepCommand final : public AbstractCommand { public: SetKeyOffToStepCommand(std::weak_ptr mod, int songNum, int trackNum, @@ -41,6 +41,6 @@ private: std::weak_ptr mod_; int song_, track_, order_, step_; - int prevNote_, prevInst_, prevVol_, prevEffVal_[4]; - std::string prevEffID_[4]; + int prevNote_, prevInst_, prevVol_; + Step::PlainEffect prevEff_[Step::N_EFFECT]; }; diff -Nru bambootracker-0.4.5+git20121209/BambooTracker/command/pattern/set_key_on_to_step_command.cpp bambootracker-0.4.6/BambooTracker/command/pattern/set_key_on_to_step_command.cpp --- bambootracker-0.4.5+git20121209/BambooTracker/command/pattern/set_key_on_to_step_command.cpp 2020-12-08 14:05:28.000000000 +0000 +++ bambootracker-0.4.6/BambooTracker/command/pattern/set_key_on_to_step_command.cpp 2021-02-11 14:37:17.000000000 +0000 @@ -25,9 +25,11 @@ #include "set_key_on_to_step_command.hpp" #include "pattern_command_utils.hpp" +#include "effect.hpp" -SetKeyOnToStepCommand::SetKeyOnToStepCommand(std::weak_ptr mod, int songNum, int trackNum, int orderNum, - int stepNum, int noteNum, bool instMask, int instNum, bool volMask, int vol, bool isFMReversed) +SetKeyOnToStepCommand::SetKeyOnToStepCommand( + std::weak_ptr mod, int songNum, int trackNum, int orderNum, int stepNum, + int noteNum, bool instMask, int instNum, bool volMask, int vol, bool isFMReversed) : AbstractCommand(CommandId::SetKeyOnToStep), mod_(mod), song_(songNum), @@ -39,7 +41,7 @@ vol_(vol), instMask_(instMask), volMask_(volMask), - isFMReserved_(isFMReversed) + isFMReversed_(isFMReversed) { Step& st = command_utils::getStep(mod, songNum, trackNum, orderNum, stepNum); prevNote_ = st.getNoteNumber(); @@ -52,7 +54,7 @@ Step& st = command_utils::getStep(mod_, song_, track_, order_, step_); st.setNoteNumber(note_); if (!instMask_) st.setInstrumentNumber(inst_); - if (!volMask_) st.setVolume((isFMReserved_ && vol_ < 0x80) ? (0x7f - vol_) : vol_); + if (!volMask_) st.setVolume(isFMReversed_ ? effect_utils::reverseFmVolume(vol_) : vol_); } void SetKeyOnToStepCommand::undo() diff -Nru bambootracker-0.4.5+git20121209/BambooTracker/command/pattern/set_key_on_to_step_command.hpp bambootracker-0.4.6/BambooTracker/command/pattern/set_key_on_to_step_command.hpp --- bambootracker-0.4.5+git20121209/BambooTracker/command/pattern/set_key_on_to_step_command.hpp 2020-12-08 14:05:28.000000000 +0000 +++ bambootracker-0.4.6/BambooTracker/command/pattern/set_key_on_to_step_command.hpp 2021-02-11 14:37:17.000000000 +0000 @@ -29,11 +29,12 @@ #include "../abstract_command.hpp" #include "module.hpp" -class SetKeyOnToStepCommand : public AbstractCommand +class SetKeyOnToStepCommand final : public AbstractCommand { public: - SetKeyOnToStepCommand(std::weak_ptr mod, int songNum, int trackNum, int orderNum, int stepNum, - int noteNum, bool instMask, int instNum, bool volMask, int vol, bool isFMReversed); + SetKeyOnToStepCommand( + std::weak_ptr mod, int songNum, int trackNum, int orderNum, int stepNum, + int noteNum, bool instMask, int instNum, bool volMask, int vol, bool isFMReversed); void redo() override; void undo() override; @@ -42,5 +43,5 @@ int song_, track_, order_, step_, note_, inst_, vol_; int prevNote_, prevInst_, prevVol_; bool instMask_, volMask_; - bool isFMReserved_; + bool isFMReversed_; }; diff -Nru bambootracker-0.4.5+git20121209/BambooTracker/command/pattern/set_volume_to_step_command.cpp bambootracker-0.4.6/BambooTracker/command/pattern/set_volume_to_step_command.cpp --- bambootracker-0.4.5+git20121209/BambooTracker/command/pattern/set_volume_to_step_command.cpp 2020-12-08 14:05:28.000000000 +0000 +++ bambootracker-0.4.6/BambooTracker/command/pattern/set_volume_to_step_command.cpp 2021-02-11 14:37:17.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (C) 2018-2020 Rerrah + * Copyright (C) 2018-2021 Rerrah * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation @@ -25,9 +25,11 @@ #include "set_volume_to_step_command.hpp" #include "pattern_command_utils.hpp" -#include "misc.hpp" +#include "effect.hpp" -SetVolumeToStepCommand::SetVolumeToStepCommand(std::weak_ptr mod, int songNum, int trackNum, int orderNum, int stepNum, int volume, bool isFMReversed, bool secondEntry) +SetVolumeToStepCommand::SetVolumeToStepCommand( + std::weak_ptr mod, int songNum, int trackNum, int orderNum, + int stepNum, int volume, bool isFMReversed, bool secondEntry) : AbstractCommand(CommandId::SetVolumeToStep), mod_(mod), song_(songNum), @@ -36,14 +38,14 @@ step_(stepNum), vol_(volume), prevVol_(command_utils::getStep(mod, songNum, trackNum, orderNum, stepNum).getVolume()), - isFMReserved_(isFMReversed), + isFMReversed_(isFMReversed), isSecondEntry_(secondEntry) { } void SetVolumeToStepCommand::redo() { - int volume = (isFMReserved_ && vol_ < 0x80) ? (0x7f - vol_) : vol_; + int volume = isFMReversed_ ? effect_utils::reverseFmVolume(vol_) : vol_; command_utils::getStep(mod_, song_, track_, order_, step_).setVolume(volume); } diff -Nru bambootracker-0.4.5+git20121209/BambooTracker/command/pattern/set_volume_to_step_command.hpp bambootracker-0.4.6/BambooTracker/command/pattern/set_volume_to_step_command.hpp --- bambootracker-0.4.5+git20121209/BambooTracker/command/pattern/set_volume_to_step_command.hpp 2020-12-08 14:05:28.000000000 +0000 +++ bambootracker-0.4.6/BambooTracker/command/pattern/set_volume_to_step_command.hpp 2021-02-11 14:37:17.000000000 +0000 @@ -29,11 +29,11 @@ #include "../abstract_command.hpp" #include "module.hpp" -class SetVolumeToStepCommand : public AbstractCommand +class SetVolumeToStepCommand final : public AbstractCommand { public: - SetVolumeToStepCommand(std::weak_ptr mod, int songNum, int trackNum, - int orderNum, int stepNum, int volume, bool isFMReversed, bool secondEntry); + SetVolumeToStepCommand(std::weak_ptr mod, int songNum, int trackNum, int orderNum, + int stepNum, int volume, bool isFMReversed, bool secondEntry); void redo() override; void undo() override; bool mergeWith(const AbstractCommand* other) override; @@ -43,6 +43,6 @@ const int song_, track_, order_, step_; int vol_; const int prevVol_; - const bool isFMReserved_; + const bool isFMReversed_; bool isSecondEntry_; }; diff -Nru bambootracker-0.4.5+git20121209/BambooTracker/command/pattern/shrink_pattern_command.cpp bambootracker-0.4.6/BambooTracker/command/pattern/shrink_pattern_command.cpp --- bambootracker-0.4.5+git20121209/BambooTracker/command/pattern/shrink_pattern_command.cpp 2020-12-08 14:05:28.000000000 +0000 +++ bambootracker-0.4.6/BambooTracker/command/pattern/shrink_pattern_command.cpp 2021-02-11 14:37:17.000000000 +0000 @@ -26,10 +26,9 @@ #include "shrink_pattern_command.hpp" #include "pattern_command_utils.hpp" -ShrinkPatternCommand::ShrinkPatternCommand(std::weak_ptr mod, - int songNum, int beginTrack, int beginColumn, - int beginOrder, int beginStep, - int endTrack, int endColumn, int endStep) +ShrinkPatternCommand::ShrinkPatternCommand( + std::weak_ptr mod, int songNum, int beginTrack, int beginColumn, + int beginOrder, int beginStep, int endTrack, int endColumn, int endStep) : AbstractCommand(CommandId::ShrinkPattern), mod_(mod), song_(songNum), @@ -59,18 +58,18 @@ case 0: st.setNoteNumber(std::stoi(prevCells_.at(i).at(j))); break; case 1: st.setInstrumentNumber(std::stoi(prevCells_.at(i).at(j))); break; case 2: st.setVolume(std::stoi(prevCells_.at(i).at(j))); break; - case 3: st.setEffectID(0, prevCells_.at(i).at(j)); break; - case 4: st.setEffectValue(0, std::stoi(prevCells_.at(i).at(j))); break; - case 5: st.setEffectID(1, prevCells_.at(i).at(j)); break; - case 6: st.setEffectValue(1, std::stoi(prevCells_.at(i).at(j))); break; - case 7: st.setEffectID(2, prevCells_.at(i).at(j)); break; - case 8: st.setEffectValue(2, std::stoi(prevCells_.at(i).at(j))); break; - case 9: st.setEffectID(3, prevCells_.at(i).at(j)); break; - case 10: st.setEffectValue(3, std::stoi(prevCells_.at(i).at(j))); break; + default: + { + int ec = c - 3; + int ei = ec / 2; + if (ec % 2) st.setEffectValue(ei, std::stoi(prevCells_.at(i).at(j))); + else st.setEffectId(ei, prevCells_.at(i).at(j)); + break; + } } - t += (++c / 11); - c %= 11; + t += (++c / Step::N_COLUMN); + c %= Step::N_COLUMN; } ++s; @@ -82,21 +81,21 @@ for (size_t j = 0; j < prevCells_.at(0).size(); ++j) { Step& st = command_utils::getStep(sng, t, order_, s); switch (c) { - case 0: st.setNoteNumber(-1); break; - case 1: st.setInstrumentNumber(-1); break; - case 2: st.setVolume(-1); break; - case 3: st.setEffectID(0, "--"); break; - case 4: st.setEffectValue(0, -1); break; - case 5: st.setEffectID(1, "--"); break; - case 6: st.setEffectValue(1, -1); break; - case 7: st.setEffectID(2, "--"); break; - case 8: st.setEffectValue(2, -1); break; - case 9: st.setEffectID(3, "--"); break; - case 10: st.setEffectValue(3, -1); break; + case 0: st.clearNoteNumber(); break; + case 1: st.clearInstrumentNumber(); break; + case 2: st.clearVolume(); break; + default: + { + int ec = c - 3; + int ei = ec / 2; + if (ec % 2) st.clearEffectValue(ei); + else st.clearEffectId(ei); + break; + } } - t += (++c / 11); - c %= 11; + t += (++c / Step::N_COLUMN); + c %= Step::N_COLUMN; } } } diff -Nru bambootracker-0.4.5+git20121209/BambooTracker/command/pattern/shrink_pattern_command.hpp bambootracker-0.4.6/BambooTracker/command/pattern/shrink_pattern_command.hpp --- bambootracker-0.4.5+git20121209/BambooTracker/command/pattern/shrink_pattern_command.hpp 2020-12-08 14:05:28.000000000 +0000 +++ bambootracker-0.4.6/BambooTracker/command/pattern/shrink_pattern_command.hpp 2021-02-11 14:37:17.000000000 +0000 @@ -31,7 +31,7 @@ #include "../abstract_command.hpp" #include "module.hpp" -class ShrinkPatternCommand : public AbstractCommand +class ShrinkPatternCommand final : public AbstractCommand { public: ShrinkPatternCommand(std::weak_ptr mod, int songNum, diff -Nru bambootracker-0.4.5+git20121209/BambooTracker/command/pattern/transpose_note_in_pattern_command.cpp bambootracker-0.4.6/BambooTracker/command/pattern/transpose_note_in_pattern_command.cpp --- bambootracker-0.4.5+git20121209/BambooTracker/command/pattern/transpose_note_in_pattern_command.cpp 2020-12-08 14:05:28.000000000 +0000 +++ bambootracker-0.4.6/BambooTracker/command/pattern/transpose_note_in_pattern_command.cpp 2021-02-11 14:37:17.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (C) 2020 Rerrah + * Copyright (C) 2020-2021 Rerrah * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation @@ -25,12 +25,12 @@ #include "transpose_note_in_pattern_command.hpp" #include "pattern_command_utils.hpp" -#include "misc.hpp" +#include "note.hpp" +#include "utils.hpp" -TransposeNoteInPatternCommand::TransposeNoteInPatternCommand(std::weak_ptr mod, - int songNum, int beginTrack, - int beginOrder, int beginStep, - int endTrack, int endStep, int seminote) +TransposeNoteInPatternCommand::TransposeNoteInPatternCommand( + std::weak_ptr mod, int songNum, int beginTrack, + int beginOrder, int beginStep, int endTrack, int endStep, int seminote) : AbstractCommand(CommandId::TransposeNoteInPattern), mod_(mod), song_(songNum), @@ -45,8 +45,8 @@ for (int step = beginStep; step <= endStep; ++step) { for (int track = beginTrack; track <= endTrack; ++track) { - int n = command_utils::getStep(sng, track, beginOrder, step).getNoteNumber(); - if (n > -1) prevKeys_.push_back(n); + Step& st = command_utils::getStep(sng, track, beginOrder, step); + if (st.hasGeneralNote()) prevKeys_.push_back(st.getNoteNumber()); } } } @@ -57,10 +57,10 @@ for (int step = bStep_; step <= eStep_; ++step) { for (int track = bTrack_; track <= eTrack_; ++track) { - Step& s = command_utils::getStep(sng, track, order_, step); - int n = s.getNoteNumber(); - if (n > -1) { - s.setNoteNumber(clamp(n + seminote_, 0, 95)); + Step& st = command_utils::getStep(sng, track, order_, step); + int n = st.getNoteNumber(); + if (st.hasGeneralNote()) { + st.setNoteNumber(utils::clamp(n + seminote_, 0, Note::NOTE_NUMBER_RANGE - 1)); } } } @@ -73,8 +73,8 @@ size_t i = 0; for (int step = bStep_; step <= eStep_; ++step) { for (int track = bTrack_; track <= eTrack_; ++track) { - Step& s = command_utils::getStep(sng, track, order_, step); - if (s.getNoteNumber() > -1) s.setNoteNumber(prevKeys_.at(i++)); + Step& st = command_utils::getStep(sng, track, order_, step); + if (st.hasGeneralNote()) st.setNoteNumber(prevKeys_.at(i++)); } } } diff -Nru bambootracker-0.4.5+git20121209/BambooTracker/command/pattern/transpose_note_in_pattern_command.hpp bambootracker-0.4.6/BambooTracker/command/pattern/transpose_note_in_pattern_command.hpp --- bambootracker-0.4.5+git20121209/BambooTracker/command/pattern/transpose_note_in_pattern_command.hpp 2020-12-08 14:05:28.000000000 +0000 +++ bambootracker-0.4.6/BambooTracker/command/pattern/transpose_note_in_pattern_command.hpp 2021-02-11 14:37:17.000000000 +0000 @@ -30,7 +30,7 @@ #include "../abstract_command.hpp" #include "module.hpp" -class TransposeNoteInPatternCommand : public AbstractCommand +class TransposeNoteInPatternCommand final : public AbstractCommand { public: TransposeNoteInPatternCommand(std::weak_ptr mod, int songNum, diff -Nru bambootracker-0.4.5+git20121209/BambooTracker/configuration.cpp bambootracker-0.4.6/BambooTracker/configuration.cpp --- bambootracker-0.4.5+git20121209/BambooTracker/configuration.cpp 2020-12-08 14:05:28.000000000 +0000 +++ bambootracker-0.4.6/BambooTracker/configuration.cpp 2021-02-11 14:37:17.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (C) 2018-2020 Rerrah + * Copyright (C) 2018-2021 Rerrah * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation @@ -26,6 +26,109 @@ #include "configuration.hpp" #include "jamming.hpp" +namespace +{ +const std::unordered_map KEY_MAP_QWERTY = { + {u8"Z", JamKey::LowC}, + {u8"S", JamKey::LowCS}, + {u8"X", JamKey::LowD}, + {u8"D", JamKey::LowDS}, + {u8"C", JamKey::LowE}, + {u8"V", JamKey::LowF}, + {u8"G", JamKey::LowFS}, + {u8"B", JamKey::LowG}, + {u8"H", JamKey::LowGS}, + {u8"N", JamKey::LowA}, + {u8"J", JamKey::LowAS}, + {u8"M", JamKey::LowB}, + {u8",", JamKey::LowC2}, + {u8"L", JamKey::LowCS2}, + {u8".", JamKey::LowD2}, + + {u8"Q", JamKey::HighC}, + {u8"2", JamKey::HighCS}, + {u8"W", JamKey::HighD}, + {u8"3", JamKey::HighDS}, + {u8"E", JamKey::HighE}, + {u8"R", JamKey::HighF}, + {u8"5", JamKey::HighFS}, + {u8"T", JamKey::HighG}, + {u8"6", JamKey::HighGS}, + {u8"Y", JamKey::HighA}, + {u8"7", JamKey::HighAS}, + {u8"U", JamKey::HighB}, + {u8"I", JamKey::HighC2}, + {u8"9", JamKey::HighCS2}, + {u8"O", JamKey::HighD2}, +}; +const std::unordered_map KEY_MAP_QWERTZ = { + {u8"Y", JamKey::LowC}, + {u8"S", JamKey::LowCS}, + {u8"X", JamKey::LowD}, + {u8"D", JamKey::LowDS}, + {u8"C", JamKey::LowE}, + {u8"V", JamKey::LowF}, + {u8"G", JamKey::LowFS}, + {u8"B", JamKey::LowG}, + {u8"H", JamKey::LowGS}, + {u8"N", JamKey::LowA}, + {u8"J", JamKey::LowAS}, + {u8"M", JamKey::LowB}, + {u8",", JamKey::LowC2}, + {u8"L", JamKey::LowCS2}, + {u8".", JamKey::LowD2}, + + {u8"Q", JamKey::HighC}, + {u8"2", JamKey::HighCS}, + {u8"W", JamKey::HighD}, + {u8"3", JamKey::HighDS}, + {u8"E", JamKey::HighE}, + {u8"R", JamKey::HighF}, + {u8"5", JamKey::HighFS}, + {u8"T", JamKey::HighG}, + {u8"6", JamKey::HighGS}, + {u8"Z", JamKey::HighA}, + {u8"7", JamKey::HighAS}, + {u8"U", JamKey::HighB}, + {u8"I", JamKey::HighC2}, + {u8"9", JamKey::HighCS2}, + {u8"O", JamKey::HighD2}, +}; +const std::unordered_map KEY_MAP_AZERTY = { + {u8"W", JamKey::LowC}, + {u8"S", JamKey::LowCS}, + {u8"X", JamKey::LowD}, + {u8"D", JamKey::LowDS}, + {u8"C", JamKey::LowE}, + {u8"V", JamKey::LowF}, + {u8"G", JamKey::LowFS}, + {u8"B", JamKey::LowG}, + {u8"H", JamKey::LowGS}, + {u8"N", JamKey::LowA}, + {u8"J", JamKey::LowAS}, + {u8",", JamKey::LowB}, + {u8";", JamKey::LowC2}, + {u8"L", JamKey::LowCS2}, + {u8".", JamKey::LowD2}, + + {u8"A", JamKey::HighC}, + {u8"É", JamKey::HighCS}, //é - \xc9 + {u8"Z", JamKey::HighD}, + {u8"\"", JamKey::HighDS}, + {u8"E", JamKey::HighE}, + {u8"R", JamKey::HighF}, + {u8"(", JamKey::HighFS}, + {u8"T", JamKey::HighG}, + {u8"-", JamKey::HighGS}, + {u8"Y", JamKey::HighA}, + {u8"È", JamKey::HighAS}, //è - \xc8 + {u8"U", JamKey::HighB}, + {u8"I", JamKey::HighC2}, + {u8"Ç", JamKey::HighCS2}, //ç - \xc7 + {u8"O", JamKey::HighD2}, +}; +} + Configuration::Configuration() { // Internal // @@ -38,7 +141,7 @@ visibleToolbar_ = true; visibleStatusBar_ = true; visibleWaveView_ = true; - pasteMode_ = PasteMode::CURSOR; + pasteMode_ = PasteMode::Cursor; // Mainwindow state mainW_ = 930; @@ -59,12 +162,12 @@ instKitH_ = 430; // Toolbar state - mainTb_.setPosition(ToolbarConfiguration::TOP_POS); + mainTb_.setPosition(ToolbarPosition::TopPosition); mainTb_.setNumber(0); mainTb_.setBreakBefore(false); mainTb_.setX(-1); // Dummy mainTb_.setY(-1); // Dummy - subTb_.setPosition(ToolbarConfiguration::TOP_POS); + subTb_.setPosition(ToolbarPosition::TopPosition); subTb_.setNumber(1); subTb_.setBreakBefore(false); subTb_.setX(-1); // Dummy @@ -102,77 +205,77 @@ // Keys shortcuts_ = { - { KeyOff, u8"-" }, - { OctaveUp, u8"Num+*" }, - { OctaveDown, u8"Num+/" }, - { EchoBuffer, u8"^" }, - { PlayAndStop, u8"Return" }, - { Play, u8"" }, - { PlayFromStart, u8"F5" }, - { PlayPattern, u8"F6" }, - { PlayFromCursor, u8"F7" }, - { PlayFromMarker, u8"Ctrl+F7" }, - { PlayStep, u8"Ctrl+Return" }, - { Stop, u8"F8" }, - { FocusOnPattern, u8"F2" }, - { FocusOnOrder, u8"F3" }, - { FocusOnInstrument, u8"F4" }, - { ToggleEditJam, u8"Space" }, - { SetMarker, u8"Ctrl+B" }, - { PasteMix, u8"Ctrl+M" }, - { PasteOverwrite, u8"" }, - { PasteInsert, u8"" }, - { SelectAll, u8"Ctrl+A" }, - { Deselect, u8"Esc" }, - { SelectRow, u8"" }, - { SelectColumn, u8"" }, - { SelectPattern, u8"" }, - { SelectOrder, u8"" }, - { GoToStep, u8"Alt+G" }, - { ToggleTrack, u8"Alt+F9" }, - { SoloTrack, u8"Alt+F10" }, - { Interpolate, u8"Ctrl+G" }, - { Reverse, u8"Ctrl+R" }, - { GoToPrevOrder, u8"Ctrl+Left" }, - { GoToNextOrder, u8"Ctrl+Right" }, - { ToggleBookmark, u8"Ctrl+K" }, - { PrevBookmark, u8"Ctrl+PgUp" }, - { NextBookmark, u8"Ctrl+PgDown" }, - { DecreaseNote, u8"Ctrl+F1" }, - { IncreaseNote, u8"Ctrl+F2" }, - { DecreaseOctave, u8"Ctrl+F3" }, - { IncreaseOctave, u8"Ctrl+F4" }, - { PrevInstrument, u8"Alt+Left" }, - { NextInstrument, u8"Alt+Right" }, - { MaskInstrument, u8"" }, - { MaskVolume, u8"" }, - { EditInstrument, u8"Ctrl+I" }, - { FollowMode, u8"ScrollLock" }, - { DuplicateOrder, u8"Ctrl+D" }, - { ClonePatterns, u8"Alt+D" }, - { CloneOrder, u8"" }, - { ReplaceInstrument, u8"Alt+S" }, - { ExpandPattern, u8"" }, - { ShrinkPattern, u8"" }, - { FineDecreaseValues, u8"Shift+F1" }, - { FineIncreaseValues, u8"Shift+F2" }, - { CoarseDecreaseValues, u8"Shift+F3" }, - { CoarseIncreaseValuse, u8"Shift+F4" }, - { ExpandEffect, u8"Alt+L" }, - { ShrinkEffect, u8"Alt+K" }, - { PrevHighlighted, u8"Ctrl+Up" }, - { NextHighlighted, u8"Ctrl+Down" }, - { IncreasePatternSize, u8"" }, - { DecreasePatternSize, u8"" }, - { IncreaseEditStep, u8"" }, - { DecreaseEditStep, u8"" }, - { DisplayEffectList, u8"F1" }, - { PreviousSong, u8"" }, - { NextSong, u8"" }, - { JamVolumeUp, u8"" }, - { JamVolumeDown, u8"" } + { ShortcutAction::KeyOff, u8"-" }, + { ShortcutAction::OctaveUp, u8"Num+*" }, + { ShortcutAction::OctaveDown, u8"Num+/" }, + { ShortcutAction::EchoBuffer, u8"^" }, + { ShortcutAction::PlayAndStop, u8"Return" }, + { ShortcutAction::Play, u8"" }, + { ShortcutAction::PlayFromStart, u8"F5" }, + { ShortcutAction::PlayPattern, u8"F6" }, + { ShortcutAction::PlayFromCursor, u8"F7" }, + { ShortcutAction::PlayFromMarker, u8"Ctrl+F7" }, + { ShortcutAction::PlayStep, u8"Ctrl+Return" }, + { ShortcutAction::Stop, u8"F8" }, + { ShortcutAction::FocusOnPattern, u8"F2" }, + { ShortcutAction::FocusOnOrder, u8"F3" }, + { ShortcutAction::FocusOnInstrument, u8"F4" }, + { ShortcutAction::ToggleEditJam, u8"Space" }, + { ShortcutAction::SetMarker, u8"Ctrl+B" }, + { ShortcutAction::PasteMix, u8"Ctrl+M" }, + { ShortcutAction::PasteOverwrite, u8"" }, + { ShortcutAction::PasteInsert, u8"" }, + { ShortcutAction::SelectAll, u8"Ctrl+A" }, + { ShortcutAction::Deselect, u8"Esc" }, + { ShortcutAction::SelectRow, u8"" }, + { ShortcutAction::SelectColumn, u8"" }, + { ShortcutAction::SelectPattern, u8"" }, + { ShortcutAction::SelectOrder, u8"" }, + { ShortcutAction::GoToStep, u8"Alt+G" }, + { ShortcutAction::ToggleTrack, u8"Alt+F9" }, + { ShortcutAction::SoloTrack, u8"Alt+F10" }, + { ShortcutAction::Interpolate, u8"Ctrl+G" }, + { ShortcutAction::Reverse, u8"Ctrl+R" }, + { ShortcutAction::GoToPrevOrder, u8"Ctrl+Left" }, + { ShortcutAction::GoToNextOrder, u8"Ctrl+Right" }, + { ShortcutAction::ToggleBookmark, u8"Ctrl+K" }, + { ShortcutAction::PrevBookmark, u8"Ctrl+PgUp" }, + { ShortcutAction::NextBookmark, u8"Ctrl+PgDown" }, + { ShortcutAction::DecreaseNote, u8"Ctrl+F1" }, + { ShortcutAction::IncreaseNote, u8"Ctrl+F2" }, + { ShortcutAction::DecreaseOctave, u8"Ctrl+F3" }, + { ShortcutAction::IncreaseOctave, u8"Ctrl+F4" }, + { ShortcutAction::PrevInstrument, u8"Alt+Left" }, + { ShortcutAction::NextInstrument, u8"Alt+Right" }, + { ShortcutAction::MaskInstrument, u8"" }, + { ShortcutAction::MaskVolume, u8"" }, + { ShortcutAction::EditInstrument, u8"Ctrl+I" }, + { ShortcutAction::FollowMode, u8"ScrollLock" }, + { ShortcutAction::DuplicateOrder, u8"Ctrl+D" }, + { ShortcutAction::ClonePatterns, u8"Alt+D" }, + { ShortcutAction::CloneOrder, u8"" }, + { ShortcutAction::ReplaceInstrument, u8"Alt+S" }, + { ShortcutAction::ExpandPattern, u8"" }, + { ShortcutAction::ShrinkPattern, u8"" }, + { ShortcutAction::FineDecreaseValues, u8"Shift+F1" }, + { ShortcutAction::FineIncreaseValues, u8"Shift+F2" }, + { ShortcutAction::CoarseDecreaseValues, u8"Shift+F3" }, + { ShortcutAction::CoarseIncreaseValuse, u8"Shift+F4" }, + { ShortcutAction::ExpandEffect, u8"Alt+L" }, + { ShortcutAction::ShrinkEffect, u8"Alt+K" }, + { ShortcutAction::PrevHighlighted, u8"Ctrl+Up" }, + { ShortcutAction::NextHighlighted, u8"Ctrl+Down" }, + { ShortcutAction::IncreasePatternSize, u8"" }, + { ShortcutAction::DecreasePatternSize, u8"" }, + { ShortcutAction::IncreaseEditStep, u8"" }, + { ShortcutAction::DecreaseEditStep, u8"" }, + { ShortcutAction::DisplayEffectList, u8"F1" }, + { ShortcutAction::PreviousSong, u8"" }, + { ShortcutAction::NextSong, u8"" }, + { ShortcutAction::JamVolumeUp, u8"" }, + { ShortcutAction::JamVolumeDown, u8"" } }; - noteEntryLayout_ = QWERTY; + noteEntryLayout_ = KeyboardLayout::QWERTY; // Sound // sndAPI_ = u8""; @@ -609,111 +712,12 @@ }; // Layouts - const std::unordered_map mappingQWERTY = { - {u8"Z", JamKey::LowC}, - {u8"S", JamKey::LowCS}, - {u8"X", JamKey::LowD}, - {u8"D", JamKey::LowDS}, - {u8"C", JamKey::LowE}, - {u8"V", JamKey::LowF}, - {u8"G", JamKey::LowFS}, - {u8"B", JamKey::LowG}, - {u8"H", JamKey::LowGS}, - {u8"N", JamKey::LowA}, - {u8"J", JamKey::LowAS}, - {u8"M", JamKey::LowB}, - {u8",", JamKey::LowC2}, - {u8"L", JamKey::LowCS2}, - {u8".", JamKey::LowD2}, - - {u8"Q", JamKey::HighC}, - {u8"2", JamKey::HighCS}, - {u8"W", JamKey::HighD}, - {u8"3", JamKey::HighDS}, - {u8"E", JamKey::HighE}, - {u8"R", JamKey::HighF}, - {u8"5", JamKey::HighFS}, - {u8"T", JamKey::HighG}, - {u8"6", JamKey::HighGS}, - {u8"Y", JamKey::HighA}, - {u8"7", JamKey::HighAS}, - {u8"U", JamKey::HighB}, - {u8"I", JamKey::HighC2}, - {u8"9", JamKey::HighCS2}, - {u8"O", JamKey::HighD2}, - }; - const std::unordered_map mappingQWERTZ = { - {u8"Y", JamKey::LowC}, - {u8"S", JamKey::LowCS}, - {u8"X", JamKey::LowD}, - {u8"D", JamKey::LowDS}, - {u8"C", JamKey::LowE}, - {u8"V", JamKey::LowF}, - {u8"G", JamKey::LowFS}, - {u8"B", JamKey::LowG}, - {u8"H", JamKey::LowGS}, - {u8"N", JamKey::LowA}, - {u8"J", JamKey::LowAS}, - {u8"M", JamKey::LowB}, - {u8",", JamKey::LowC2}, - {u8"L", JamKey::LowCS2}, - {u8".", JamKey::LowD2}, - - {u8"Q", JamKey::HighC}, - {u8"2", JamKey::HighCS}, - {u8"W", JamKey::HighD}, - {u8"3", JamKey::HighDS}, - {u8"E", JamKey::HighE}, - {u8"R", JamKey::HighF}, - {u8"5", JamKey::HighFS}, - {u8"T", JamKey::HighG}, - {u8"6", JamKey::HighGS}, - {u8"Z", JamKey::HighA}, - {u8"7", JamKey::HighAS}, - {u8"U", JamKey::HighB}, - {u8"I", JamKey::HighC2}, - {u8"9", JamKey::HighCS2}, - {u8"O", JamKey::HighD2}, - }; - const std::unordered_map mappingAZERTY = { - {u8"W", JamKey::LowC}, - {u8"S", JamKey::LowCS}, - {u8"X", JamKey::LowD}, - {u8"D", JamKey::LowDS}, - {u8"C", JamKey::LowE}, - {u8"V", JamKey::LowF}, - {u8"G", JamKey::LowFS}, - {u8"B", JamKey::LowG}, - {u8"H", JamKey::LowGS}, - {u8"N", JamKey::LowA}, - {u8"J", JamKey::LowAS}, - {u8",", JamKey::LowB}, - {u8";", JamKey::LowC2}, - {u8"L", JamKey::LowCS2}, - {u8".", JamKey::LowD2}, - - {u8"A", JamKey::HighC}, - {u8"É", JamKey::HighCS}, //é - \xc9 - {u8"Z", JamKey::HighD}, - {u8"\"", JamKey::HighDS}, - {u8"E", JamKey::HighE}, - {u8"R", JamKey::HighF}, - {u8"(", JamKey::HighFS}, - {u8"T", JamKey::HighG}, - {u8"-", JamKey::HighGS}, - {u8"Y", JamKey::HighA}, - {u8"È", JamKey::HighAS}, //è - \xc8 - {u8"U", JamKey::HighB}, - {u8"I", JamKey::HighC2}, - {u8"Ç", JamKey::HighCS2}, //ç - \xc7 - {u8"O", JamKey::HighD2}, - }; - mappingCustom = {}; + mappingCustom_ = {}; mappingLayouts = { - { Custom, mappingCustom }, - { QWERTY, mappingQWERTY }, - { QWERTZ, mappingQWERTZ }, - { AZERTY, mappingAZERTY } + { KeyboardLayout::Custom, mappingCustom_ }, + { KeyboardLayout::QWERTY, KEY_MAP_QWERTY }, + { KeyboardLayout::QWERTZ, KEY_MAP_QWERTZ }, + { KeyboardLayout::AZERTY, KEY_MAP_AZERTY } }; // Appearance @@ -727,237 +731,8 @@ odrRowFontSize_ = 10; } -// Internal // -void Configuration::setFollowMode(bool enabled) { followMode_ = enabled; } - -bool Configuration::getFollowMode() const { return followMode_; } - -void Configuration::setWorkingDirectory(std::string path) { workDir_ = path; } - -std::string Configuration::getWorkingDirectory() const { return workDir_; } - -void Configuration::setInstrumentOpenFormat(int i) { instOpenFormat_ = i; } - -int Configuration::getInstrumentOpenFormat() const { return instOpenFormat_; } - -void Configuration::setBankOpenFormat(int i) { bankOpenFormat_ = i; } - -int Configuration::getBankOpenFormat() const { return bankOpenFormat_; } - -void Configuration::setInstrumentMask(bool enabled) { instMask_ = enabled; } - -bool Configuration::getInstrumentMask() const { return instMask_; } - -void Configuration::setVolumeMask(bool enabled) { volMask_ = enabled; } - -bool Configuration::getVolumeMask() const { return volMask_; } - -void Configuration::setVisibleToolbar(bool visible) { visibleToolbar_ = visible; } - -bool Configuration::getVisibleToolbar() const { return visibleToolbar_; } - -void Configuration::setVisibleStatusBar(bool visible) { visibleStatusBar_ = visible; } - -bool Configuration::getVisibleStatusBar() const { return visibleStatusBar_; } - -void Configuration::setVisibleWaveView(bool visible) { visibleWaveView_ = visible; } - -bool Configuration::getVisibleWaveView() const { return visibleWaveView_; } - -void Configuration::setPasteMode(PasteMode mode) { pasteMode_ = mode; } - -Configuration::PasteMode Configuration::getPasteMode() const { return pasteMode_; } - -// Mainwindow state -void Configuration::setMainWindowWidth(int w) { mainW_ = w; } - -int Configuration::getMainWindowWidth() const { return mainW_; } - -void Configuration::setMainWindowHeight(int h) { mainH_ = h; } - -int Configuration::getMainWindowHeight() const { return mainH_; } - -void Configuration::setMainWindowMaximized(bool isMax) { mainMax_ = isMax; } - -bool Configuration::getMainWindowMaximized() const { return mainMax_; } - -void Configuration::setMainWindowX(int x) { mainX_ = x; } - -int Configuration::getMainWindowX() const { return mainX_; } - -void Configuration::setMainWindowY(int y) { mainY_ = y; } - -int Configuration::getMainWindowY() const { return mainY_; } - -void Configuration::setMainWindowVerticalSplit(int y) { mainVSplit_ = y; } - -int Configuration::getMainWindowVerticalSplit() const { return mainVSplit_; } - -// Instrument editor state -void Configuration::setInstrumentFMWindowWidth(int w) { instFMW_ = w; } - -int Configuration::getInstrumentFMWindowWidth() const { return instFMW_; } - -void Configuration::setInstrumentFMWindowHeight(int h) { instFMH_ = h; } - -int Configuration::getInstrumentFMWindowHeight() const { return instFMH_; } - -void Configuration::setInstrumentSSGWindowWidth(int w) { instSSGW_ = w; } - -int Configuration::getInstrumentSSGWindowWidth() const { return instSSGW_; } - -void Configuration::setInstrumentSSGWindowHeight(int h) { instSSGH_ = h; } - -int Configuration::getInstrumentSSGWindowHeight() const { return instSSGH_; } - -void Configuration::setInstrumentADPCMWindowWidth(int w) { instADPCMW_ = w; } - -int Configuration::getInstrumentADPCMWindowWidth() const { return instADPCMW_; } - -void Configuration::setInstrumentADPCMWindowHeight(int h) { instADPCMH_ = h; } - -int Configuration::getInstrumentADPCMWindowHeight() const { return instADPCMH_; } - -void Configuration::setInstrumentDrumkitWindowWidth(int w) { instKitW_ = w; } - -int Configuration::getInstrumentDrumkitWindowWidth() const { return instKitW_; } - -void Configuration::setInstrumentDrumkitWindowHeight(int h) { instKitH_ = h; } - -int Configuration::getInstrumentDrumkitWindowHeight() const { return instKitH_; } - -// Toolbar state -using TBConfig = Configuration::ToolbarConfiguration; -void TBConfig::setPosition(ToolbarPosition pos) { pos_ = pos; } - -TBConfig::ToolbarPosition TBConfig::getPosition() const { return pos_; } - -void TBConfig::setNumber(int n) { num_ = n; } - -int TBConfig::getNumber() const { return num_; } - -void TBConfig::setBreakBefore(bool enabled) { hasBreakBefore_ = enabled; } - -bool TBConfig::hasBreakBefore() const { return hasBreakBefore_; } - -void TBConfig::setX(int x) { x_ = x; } - -int TBConfig::getX() const { return x_; } - -void TBConfig::setY(int y) { y_ = y; } - -int TBConfig::getY() const { return y_; } - -TBConfig& Configuration::getMainToolbarConfiguration() { return mainTb_; } - -TBConfig& Configuration::getSubToolbarConfiguration() { return subTb_; } - -// General // -// General settings -void Configuration::setWarpCursor(bool enabled) { warpCursor_ = enabled; } - -bool Configuration::getWarpCursor() const { return warpCursor_; } - -void Configuration::setWarpAcrossOrders(bool enabled) { warpAcrossOrders_ = enabled; } - -bool Configuration::getWarpAcrossOrders() const { return warpAcrossOrders_; } - -void Configuration::setShowRowNumberInHex(bool enabled) { showRowNumHex_ = enabled; } - -bool Configuration::getShowRowNumberInHex() const { return showRowNumHex_; } - -void Configuration::setShowPreviousNextOrders(bool enabled) { showPrevNextOrders_ = enabled; } - -bool Configuration::getShowPreviousNextOrders() const { return showPrevNextOrders_; } - -void Configuration::setBackupModules(bool enabled) { backupModules_ = enabled; } - -bool Configuration::getBackupModules() const { return backupModules_; } - -void Configuration::setDontSelectOnDoubleClick(bool enabled) { dontSelectOnDoubleClick_ = enabled; } - -bool Configuration::getDontSelectOnDoubleClick() const { return dontSelectOnDoubleClick_; } - -void Configuration::setReverseFMVolumeOrder(bool enabled) { reverseFMVolumeOrder_= enabled; } - -bool Configuration::getReverseFMVolumeOrder() const { return reverseFMVolumeOrder_; } - -void Configuration::setMoveCursorToRight(bool enabled) { moveCursorToRight_ = enabled; } - -bool Configuration::getMoveCursorToRight() const { return moveCursorToRight_; } - -void Configuration::setRetrieveChannelState(bool enabled) { retrieveChannelState_ = enabled; } - -bool Configuration::getRetrieveChannelState() const { return retrieveChannelState_; } - -void Configuration::setEnableTranslation(bool enabled) { enableTranslation_ = enabled; } - -bool Configuration::getEnableTranslation() const { return enableTranslation_; } - -void Configuration::setShowFMDetuneAsSigned(bool enabled) { showFMDetuneSigned_ = enabled; } - -bool Configuration::getShowFMDetuneAsSigned() const { return showFMDetuneSigned_; } - -void Configuration::setFill00ToEffectValue(bool enabled) { fill00ToEffectValue_ = enabled; } - -bool Configuration::getFill00ToEffectValue() const { return fill00ToEffectValue_; } - -void Configuration::setMoveCursorByHorizontalScroll(bool enabled) { moveCursorHScroll_ = enabled; } - -bool Configuration::getMoveCursorByHorizontalScroll() const { return moveCursorHScroll_; } - -void Configuration::setOverwriteUnusedUneditedPropety(bool enabled) { overwriteUnusedUnedited_ = enabled; } - -bool Configuration::getOverwriteUnusedUneditedPropety() const { return overwriteUnusedUnedited_; } - -void Configuration::setWriteOnlyUsedSamples(bool enabled) { writeOnlyUsedSamples_ = enabled; } - -bool Configuration::getWriteOnlyUsedSamples() const { return writeOnlyUsedSamples_; } - -void Configuration::setReflectInstrumentNumberChange(bool enabled) { reflectInstNumChange_ = enabled; } - -bool Configuration::getReflectInstrumentNumberChange() const { return reflectInstNumChange_; } - -void Configuration::setFixJammingVolume(bool enabled) { fixJamVol_ = enabled; } - -bool Configuration::getFixJammingVolume() const { return fixJamVol_; } - -void Configuration::setMuteHiddenTracks(bool enabled) { muteHiddenTracks_ = enabled; } - -bool Configuration::getMuteHiddenTracks() const { return muteHiddenTracks_; } - -void Configuration::setRestoreTrackVisibility(bool enabled) { restoreTrackVis_ = enabled; } - -bool Configuration::getRestoreTrackVisibility() const { return restoreTrackVis_; } - -// Edit settings -void Configuration::setPageJumpLength(size_t length) { pageJumpLength_ = length; } - -size_t Configuration::getPageJumpLength() const { return pageJumpLength_; } - -void Configuration::setEditableStep(size_t step) { editableStep_ = step; } - -size_t Configuration::getEditableStep() const { return editableStep_; } - -void Configuration::setKeyRepetition(bool enabled) { keyRepetision_ = enabled; } - -bool Configuration::getKeyRepetition() const { return keyRepetision_; } - -// Wave view -void Configuration::setWaveViewFrameRate(int rate) { waveViewFps_ = rate; } - -int Configuration::getWaveViewFrameRate() const { return waveViewFps_; } - // Keys -void Configuration::setShortcuts(std::unordered_map shortcuts) { shortcuts_ = shortcuts; } - -std::unordered_map Configuration::getShortcuts() const { return shortcuts_; } - -void Configuration::setNoteEntryLayout(KeyboardLayout layout) { noteEntryLayout_ = layout; } - -Configuration::KeyboardLayout Configuration::getNoteEntryLayout() const { return noteEntryLayout_; } - -void Configuration::setCustomLayoutKeys(std::unordered_map mapping) +void Configuration::setCustomLayoutKeys(const std::unordered_map& mapping) { mappingLayouts[KeyboardLayout::Custom] = mapping; } @@ -966,92 +741,3 @@ { return mappingLayouts.at(KeyboardLayout::Custom); } - -// Sound // -void Configuration::setSoundAPI(std::string api) { sndAPI_ = api; } - -std::string Configuration::getSoundAPI() const { return sndAPI_; } - -void Configuration::setSoundDevice(std::string device) { sndDevice_ = device; } - -std::string Configuration::getSoundDevice() const { return sndDevice_; } - -void Configuration::setRealChipInterface(RealChipInterface type) { realChip_ = type; } - -RealChipInterface Configuration::getRealChipInterface() const { return realChip_; } - -void Configuration::setEmulator(int emulator) { emulator_ = emulator; } - -int Configuration::getEmulator() const { return emulator_; } - -void Configuration::setSampleRate(uint32_t rate) { sampleRate_ = rate; } - -uint32_t Configuration::getSampleRate() const { return sampleRate_; } - -void Configuration::setBufferLength(size_t length) { bufferLength_ = length; } - -size_t Configuration::getBufferLength() const { return bufferLength_; } - -// Midi // -void Configuration::setMidiEnabled(const bool enabled) { midiEnabled_ = enabled; } - -bool Configuration::getMidiEnabled() const { return midiEnabled_; } - -void Configuration::setMidiAPI(const std::string& api) { midiAPI_ = api; } - -std::string Configuration::getMidiAPI() const { return midiAPI_; } - -void Configuration::setMidiInputPort(const std::string& port) { midiInPort_ = port; } - -std::string Configuration::getMidiInputPort() const { return midiInPort_; } - -// Mixer // -void Configuration::setMixerVolumeMaster(int percentage) { mixerVolumeMaster_ = percentage; } - -int Configuration::getMixerVolumeMaster() const { return mixerVolumeMaster_; } - -void Configuration::setMixerVolumeFM(double dB) { mixerVolumeFM_ = dB; } - -double Configuration::getMixerVolumeFM() const { return mixerVolumeFM_; } - -void Configuration::setMixerVolumeSSG(double dB) { mixerVolumeSSG_ = dB; } - -double Configuration::getMixerVolumeSSG() const { return mixerVolumeSSG_; } - -// Input // -void Configuration::setFMEnvelopeTexts(std::vector texts) { fmEnvelopeTexts_ = texts; } - -std::vector Configuration::getFMEnvelopeTexts() const { return fmEnvelopeTexts_; } - -// Appearrance -void Configuration::setPatternEditorHeaderFont(std::string font) { ptnHdFont_ = font; } - -std::string Configuration::getPatternEditorHeaderFont() const { return ptnHdFont_; } - -void Configuration::setPatternEditorHeaderFontSize(int size) { ptnHdFontSize_ = size; } - -int Configuration::getPatternEditorHeaderFontSize() const { return ptnHdFontSize_; } - -void Configuration::setPatternEditorRowsFont(std::string font) { ptnRowFont_ = font; } - -std::string Configuration::getPatternEditorRowsFont() const { return ptnRowFont_; } - -void Configuration::setPatternEditorRowsFontSize(int size) { ptnRowFontSize_ = size; } - -int Configuration::getPatternEditorRowsFontSize() const { return ptnRowFontSize_; } - -void Configuration::setOrderListHeaderFont(std::string font) { odrHdFont_ = font; } - -std::string Configuration::getOrderListHeaderFont() const { return odrHdFont_; } - -void Configuration::setOrderListHeaderFontSize(int size) { odrHdFontSize_ = size; } - -int Configuration::getOrderListHeaderFontSize() const { return odrHdFontSize_; } - -void Configuration::setOrderListRowsFont(std::string font) { odrRowFont_ = font; } - -std::string Configuration::getOrderListRowsFont() const { return odrRowFont_; } - -void Configuration::setOrderListRowsFontSize(int size) { odrRowFontSize_ = size; } - -int Configuration::getOrderListRowsFontSize() const { return odrRowFontSize_; } diff -Nru bambootracker-0.4.5+git20121209/BambooTracker/configuration.hpp bambootracker-0.4.6/BambooTracker/configuration.hpp --- bambootracker-0.4.5+git20121209/BambooTracker/configuration.hpp 2020-12-08 14:05:28.000000000 +0000 +++ bambootracker-0.4.6/BambooTracker/configuration.hpp 2021-02-11 14:37:17.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (C) 2018-2020 Rerrah + * Copyright (C) 2018-2021 Rerrah * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation @@ -31,16 +31,31 @@ #include #include #include "enum_hash.hpp" -#include "misc.hpp" enum class JamKey : int; +enum class FMEnvelopeTextType : int +{ + Skip, AL, FB, + AR1, DR1, SR1, RR1, SL1, TL1, KS1, ML1, DT1, + AR2, DR2, SR2, RR2, SL2, TL2, KS2, ML2, DT2, + AR3, DR3, SR3, RR3, SL3, TL3, KS3, ML3, DT3, + AR4, DR4, SR4, RR4, SL4, TL4, KS4, ML4, DT4 +}; + struct FMEnvelopeText { std::string name; std::vector texts; }; +enum class RealChipInterface : int +{ + NONE = 0, + SCCI = 1, + C86CTL = 2 +}; + class Configuration { public: @@ -48,27 +63,27 @@ // Internal // public: - void setFollowMode(bool enabled); - bool getFollowMode() const; - void setWorkingDirectory(std::string path); - std::string getWorkingDirectory() const; - void setInstrumentOpenFormat(int i); - int getInstrumentOpenFormat() const; - void setBankOpenFormat(int i); - int getBankOpenFormat() const; - void setInstrumentMask(bool enabled); - bool getInstrumentMask() const; - void setVolumeMask(bool enabled); - bool getVolumeMask() const; - void setVisibleToolbar(bool visible); - bool getVisibleToolbar() const; - void setVisibleStatusBar(bool visible); - bool getVisibleStatusBar() const; - void setVisibleWaveView(bool visible); - bool getVisibleWaveView() const; - enum PasteMode { CURSOR = 0, SELECTION, FILL }; - void setPasteMode(PasteMode mode); - PasteMode getPasteMode() const; + void setFollowMode(bool enabled) { followMode_ = enabled; } + bool getFollowMode() const { return followMode_; } + void setWorkingDirectory(const std::string& path) { workDir_ = path; } + std::string getWorkingDirectory() const { return workDir_; } + void setInstrumentOpenFormat(int i) { instOpenFormat_ = i; } + int getInstrumentOpenFormat() const { return instOpenFormat_; } + void setBankOpenFormat(int i) { bankOpenFormat_ = i; } + int getBankOpenFormat() const { return bankOpenFormat_; } + void setInstrumentMask(bool enabled) { instMask_ = enabled; } + bool getInstrumentMask() const { return instMask_; } + void setVolumeMask(bool enabled) { volMask_ = enabled; } + bool getVolumeMask() const { return volMask_; } + void setVisibleToolbar(bool visible) { visibleToolbar_ = visible; } + bool getVisibleToolbar() const { return visibleToolbar_; } + void setVisibleStatusBar(bool visible) { visibleStatusBar_ = visible; } + bool getVisibleStatusBar() const { return visibleStatusBar_; } + void setVisibleWaveView(bool visible) { visibleWaveView_ = visible; } + bool getVisibleWaveView() const { return visibleWaveView_; } + enum class PasteMode : int { Cursor, Selection, Fill }; + void setPasteMode(PasteMode mode) { pasteMode_ = mode; } + PasteMode getPasteMode() const { return pasteMode_; } private: bool followMode_; std::string workDir_; @@ -79,18 +94,18 @@ // Mainwindow state public: - void setMainWindowWidth(int w); - int getMainWindowWidth() const; - void setMainWindowHeight(int h); - int getMainWindowHeight() const; - void setMainWindowMaximized(bool isMax); - bool getMainWindowMaximized() const; - void setMainWindowX(int x); - int getMainWindowX() const; - void setMainWindowY(int y); - int getMainWindowY() const; - void setMainWindowVerticalSplit(int y); - int getMainWindowVerticalSplit() const; + void setMainWindowWidth(int w) { mainW_ = w; } + int getMainWindowWidth() const { return mainW_; } + void setMainWindowHeight(int h) { mainH_ = h; } + int getMainWindowHeight() const { return mainH_; } + void setMainWindowMaximized(bool isMax) { mainMax_ = isMax; } + bool getMainWindowMaximized() const { return mainMax_; } + void setMainWindowX(int x) { mainX_ = x; } + int getMainWindowX() const { return mainX_; } + void setMainWindowY(int y) { mainY_ = y; } + int getMainWindowY() const { return mainY_; } + void setMainWindowVerticalSplit(int y) { mainVSplit_ = y; } + int getMainWindowVerticalSplit() const { return mainVSplit_; } private: int mainW_, mainH_; bool mainMax_; @@ -99,22 +114,22 @@ // Instrument editor state public: - void setInstrumentFMWindowWidth(int w); - int getInstrumentFMWindowWidth() const; - void setInstrumentFMWindowHeight(int h); - int getInstrumentFMWindowHeight() const; - void setInstrumentSSGWindowWidth(int w); - int getInstrumentSSGWindowWidth() const; - void setInstrumentSSGWindowHeight(int h); - int getInstrumentSSGWindowHeight() const; - void setInstrumentADPCMWindowWidth(int w); - int getInstrumentADPCMWindowWidth() const; - void setInstrumentADPCMWindowHeight(int h); - int getInstrumentADPCMWindowHeight() const; - void setInstrumentDrumkitWindowWidth(int w); - int getInstrumentDrumkitWindowWidth() const; - void setInstrumentDrumkitWindowHeight(int h); - int getInstrumentDrumkitWindowHeight() const; + void setInstrumentFMWindowWidth(int w) { instFMW_ = w; } + int getInstrumentFMWindowWidth() const { return instFMW_; } + void setInstrumentFMWindowHeight(int h) { instFMH_ = h; } + int getInstrumentFMWindowHeight() const { return instFMH_; } + void setInstrumentSSGWindowWidth(int w) { instSSGW_ = w; } + int getInstrumentSSGWindowWidth() const { return instSSGW_; } + void setInstrumentSSGWindowHeight(int h) { instSSGH_ = h; } + int getInstrumentSSGWindowHeight() const { return instSSGH_; } + void setInstrumentADPCMWindowWidth(int w) { instADPCMW_ = w; } + int getInstrumentADPCMWindowWidth() const { return instADPCMW_; } + void setInstrumentADPCMWindowHeight(int h) { instADPCMH_ = h; } + int getInstrumentADPCMWindowHeight() const { return instADPCMH_; } + void setInstrumentDrumkitWindowWidth(int w) { instKitW_ = w; } + int getInstrumentDrumkitWindowWidth() const { return instKitW_; } + void setInstrumentDrumkitWindowHeight(int h) { instKitH_ = h; } + int getInstrumentDrumkitWindowHeight() const { return instKitH_; } private: int instFMW_, instFMH_; int instSSGW_, instSSGH_; @@ -126,69 +141,70 @@ class ToolbarConfiguration { public: - enum ToolbarPosition : int { TOP_POS = 0, BOTTOM_POS, LEFT_POS, RIGHT_POS, FLOAT_POS }; - void setPosition(ToolbarPosition pos); - ToolbarPosition getPosition() const; - void setNumber(int n); - int getNumber() const; - void setBreakBefore(bool enabled); - bool hasBreakBefore() const; - void setX(int x); - int getX() const; - void setY(int y); - int getY() const; + enum class Position : int { TopPosition = 0, BottomPosition, LeftPosition, RightPosition, FloatPorition }; + void setPosition(Position pos) { pos_ = pos; } + Position getPosition() const { return pos_; } + void setNumber(int n) { num_ = n; } + int getNumber() const { return num_; } + void setBreakBefore(bool enabled) { hasBreakBefore_ = enabled; } + bool hasBreakBefore() const { return hasBreakBefore_; } + void setX(int x) { x_ = x; } + int getX() const { return x_; } + void setY(int y) { y_ = y; } + int getY() const { return y_; } private: - ToolbarPosition pos_; + Position pos_; int num_; bool hasBreakBefore_; int x_, y_; }; - ToolbarConfiguration& getMainToolbarConfiguration(); - ToolbarConfiguration& getSubToolbarConfiguration(); + using ToolbarPosition = ToolbarConfiguration::Position; + ToolbarConfiguration& getMainToolbarConfiguration() { return mainTb_; } + ToolbarConfiguration& getSubToolbarConfiguration() { return subTb_; } private: ToolbarConfiguration mainTb_, subTb_; // General // // General settings public: - void setWarpCursor(bool enabled); - bool getWarpCursor() const; - void setWarpAcrossOrders(bool enabled); - bool getWarpAcrossOrders() const; - void setShowRowNumberInHex(bool enabled); - bool getShowRowNumberInHex() const; - void setShowPreviousNextOrders(bool enabled); - bool getShowPreviousNextOrders() const; - void setBackupModules(bool enabled); - bool getBackupModules() const; - void setDontSelectOnDoubleClick(bool enabled); - bool getDontSelectOnDoubleClick() const; - void setReverseFMVolumeOrder(bool enabled); - bool getReverseFMVolumeOrder() const; - void setMoveCursorToRight(bool enabled); - bool getMoveCursorToRight() const; - void setRetrieveChannelState(bool enabled); - bool getRetrieveChannelState() const; - void setEnableTranslation(bool enabled); - bool getEnableTranslation() const; - void setShowFMDetuneAsSigned(bool enabled); - bool getShowFMDetuneAsSigned() const; - void setFill00ToEffectValue(bool enabled); - bool getFill00ToEffectValue() const; - void setMoveCursorByHorizontalScroll(bool enabled); - bool getMoveCursorByHorizontalScroll() const; - void setOverwriteUnusedUneditedPropety(bool enabled); - bool getOverwriteUnusedUneditedPropety() const; - void setWriteOnlyUsedSamples(bool enabled); - bool getWriteOnlyUsedSamples() const; - void setReflectInstrumentNumberChange(bool enabled); - bool getReflectInstrumentNumberChange() const; - void setFixJammingVolume(bool enabled); - bool getFixJammingVolume() const; - void setMuteHiddenTracks(bool enabled); - bool getMuteHiddenTracks() const; - void setRestoreTrackVisibility(bool enabled); - bool getRestoreTrackVisibility() const; + void setWarpCursor(bool enabled) { warpCursor_ = enabled; } + bool getWarpCursor() const { return warpCursor_; } + void setWarpAcrossOrders(bool enabled) { warpAcrossOrders_ = enabled; } + bool getWarpAcrossOrders() const { return warpAcrossOrders_; } + void setShowRowNumberInHex(bool enabled) { showRowNumHex_ = enabled; } + bool getShowRowNumberInHex() const { return showRowNumHex_; } + void setShowPreviousNextOrders(bool enabled) { showPrevNextOrders_ = enabled; } + bool getShowPreviousNextOrders() const { return showPrevNextOrders_; } + void setBackupModules(bool enabled) { backupModules_ = enabled; } + bool getBackupModules() const { return backupModules_; } + void setDontSelectOnDoubleClick(bool enabled) { dontSelectOnDoubleClick_ = enabled; } + bool getDontSelectOnDoubleClick() const { return dontSelectOnDoubleClick_; } + void setReverseFMVolumeOrder(bool enabled) { reverseFMVolumeOrder_= enabled; } + bool getReverseFMVolumeOrder() const { return reverseFMVolumeOrder_; } + void setMoveCursorToRight(bool enabled) { moveCursorToRight_ = enabled; } + bool getMoveCursorToRight() const { return moveCursorToRight_; } + void setRetrieveChannelState(bool enabled) { retrieveChannelState_ = enabled; } + bool getRetrieveChannelState() const { return retrieveChannelState_; } + void setEnableTranslation(bool enabled) { enableTranslation_ = enabled; } + bool getEnableTranslation() const { return enableTranslation_; } + void setShowFMDetuneAsSigned(bool enabled) { showFMDetuneSigned_ = enabled; } + bool getShowFMDetuneAsSigned() const { return showFMDetuneSigned_; } + void setFill00ToEffectValue(bool enabled) { fill00ToEffectValue_ = enabled; } + bool getFill00ToEffectValue() const { return fill00ToEffectValue_; } + void setMoveCursorByHorizontalScroll(bool enabled) { moveCursorHScroll_ = enabled; } + bool getMoveCursorByHorizontalScroll() const { return moveCursorHScroll_; } + void setOverwriteUnusedUneditedPropety(bool enabled) { overwriteUnusedUnedited_ = enabled; } + bool getOverwriteUnusedUneditedPropety() const { return overwriteUnusedUnedited_; } + void setWriteOnlyUsedSamples(bool enabled) { writeOnlyUsedSamples_ = enabled; } + bool getWriteOnlyUsedSamples() const { return writeOnlyUsedSamples_; } + void setReflectInstrumentNumberChange(bool enabled) { reflectInstNumChange_ = enabled; } + bool getReflectInstrumentNumberChange() const { return reflectInstNumChange_; } + void setFixJammingVolume(bool enabled) { fixJamVol_ = enabled; } + bool getFixJammingVolume() const { return fixJamVol_; } + void setMuteHiddenTracks(bool enabled) { muteHiddenTracks_ = enabled; } + bool getMuteHiddenTracks() const { return muteHiddenTracks_; } + void setRestoreTrackVisibility(bool enabled) { restoreTrackVis_ = enabled; } + bool getRestoreTrackVisibility() const { return restoreTrackVis_; } private: bool warpCursor_, warpAcrossOrders_, showRowNumHex_, showPrevNextOrders_, backupModules_; bool dontSelectOnDoubleClick_, reverseFMVolumeOrder_, moveCursorToRight_, retrieveChannelState_; @@ -198,26 +214,26 @@ // Edit settings public: - void setPageJumpLength(size_t length); - size_t getPageJumpLength() const; - void setEditableStep(size_t step); - size_t getEditableStep() const; - void setKeyRepetition(bool enabled); - bool getKeyRepetition() const; + void setPageJumpLength(size_t length) { pageJumpLength_ = length; } + size_t getPageJumpLength() const { return pageJumpLength_; } + void setEditableStep(size_t step) { editableStep_ = step; } + size_t getEditableStep() const { return editableStep_; } + void setKeyRepetition(bool enabled) { keyRepetision_ = enabled; } + bool getKeyRepetition() const { return keyRepetision_; } private: size_t pageJumpLength_, editableStep_; bool keyRepetision_; // Wave view public: - void setWaveViewFrameRate(int rate); - int getWaveViewFrameRate() const; + void setWaveViewFrameRate(int rate) { waveViewFps_ = rate; } + int getWaveViewFrameRate() const { return waveViewFps_; } private: int waveViewFps_; // Keys public: - enum ShortcutAction : int + enum class ShortcutAction : int { KeyOff, OctaveUp, OctaveDown, EchoBuffer, PlayAndStop, Play, PlayFromStart, PlayPattern, PlayFromCursor, PlayFromMarker, PlayStep, Stop, FocusOnPattern, FocusOnOrder, FocusOnInstrument, @@ -231,9 +247,9 @@ IncreasePatternSize, DecreasePatternSize, IncreaseEditStep, DecreaseEditStep, DisplayEffectList, PreviousSong, NextSong, JamVolumeUp, JamVolumeDown }; - void setShortcuts(std::unordered_map shortcuts); - std::unordered_map getShortcuts() const; - enum KeyboardLayout : int + void setShortcuts(std::unordered_map shortcuts) { shortcuts_ = shortcuts; } + std::unordered_map getShortcuts() const { return shortcuts_; } + enum class KeyboardLayout : int { // at the top, so new layouts can easily be added in after it // and it's always easy to find no matter how many layouts we add @@ -242,32 +258,31 @@ QWERTZ, AZERTY }; - static const std::unordered_map mappingQWERTY, mappingQWERTZ, mappingAZERTY; - std::unordered_map mappingCustom; std::unordered_map> mappingLayouts; - void setNoteEntryLayout(KeyboardLayout layout); - KeyboardLayout getNoteEntryLayout() const; - void setCustomLayoutKeys(std::unordered_map mapping); + void setNoteEntryLayout(KeyboardLayout layout) { noteEntryLayout_ = layout; } + KeyboardLayout getNoteEntryLayout() const { return noteEntryLayout_; } + void setCustomLayoutKeys(const std::unordered_map& mapping); std::unordered_map getCustomLayoutKeys() const; private: std::unordered_map shortcuts_; KeyboardLayout noteEntryLayout_; + std::unordered_map mappingCustom_; // Sound // public: - void setSoundAPI(std::string api); - std::string getSoundAPI() const; - void setSoundDevice(std::string device); - std::string getSoundDevice() const; - void setRealChipInterface(RealChipInterface type); - RealChipInterface getRealChipInterface() const; - void setEmulator(int emulator); - int getEmulator() const; - void setSampleRate(uint32_t rate); - uint32_t getSampleRate() const; - void setBufferLength(size_t length); - size_t getBufferLength() const; + void setSoundAPI(const std::string& api) { sndAPI_ = api; } + std::string getSoundAPI() const { return sndAPI_; } + void setSoundDevice(const std::string& device) { sndDevice_ = device; } + std::string getSoundDevice() const { return sndDevice_; } + void setRealChipInterface(RealChipInterface type) { realChip_ = type; } + RealChipInterface getRealChipInterface() const { return realChip_; } + void setEmulator(int emulator) { emulator_ = emulator; } + int getEmulator() const { return emulator_; } + void setSampleRate(uint32_t rate) { sampleRate_ = rate; } + uint32_t getSampleRate() const { return sampleRate_; } + void setBufferLength(size_t length) { bufferLength_ = length; } + size_t getBufferLength() const { return bufferLength_; } private: std::string sndAPI_, sndDevice_; RealChipInterface realChip_; @@ -277,51 +292,51 @@ // Midi // public: - void setMidiEnabled(const bool enabled); - bool getMidiEnabled() const; - void setMidiAPI(const std::string& api); - std::string getMidiAPI() const; - void setMidiInputPort(const std::string& port); - std::string getMidiInputPort() const; + void setMidiEnabled(const bool enabled) { midiEnabled_ = enabled; } + bool getMidiEnabled() const { return midiEnabled_; } + void setMidiAPI(const std::string& api) { midiAPI_ = api; } + std::string getMidiAPI() const { return midiAPI_; } + void setMidiInputPort(const std::string& port) { midiInPort_ = port; } + std::string getMidiInputPort() const { return midiInPort_; } private: bool midiEnabled_; std::string midiAPI_, midiInPort_; // Mixer // public: - void setMixerVolumeMaster(int percentage); - int getMixerVolumeMaster() const; - void setMixerVolumeFM(double dB); - double getMixerVolumeFM() const; - void setMixerVolumeSSG(double dB); - double getMixerVolumeSSG() const; + void setMixerVolumeMaster(int percentage) { mixerVolumeMaster_ = percentage; } + int getMixerVolumeMaster() const { return mixerVolumeMaster_; } + void setMixerVolumeFM(double dB) { mixerVolumeFM_ = dB; } + double getMixerVolumeFM() const { return mixerVolumeFM_; } + void setMixerVolumeSSG(double dB) { mixerVolumeSSG_ = dB; } + double getMixerVolumeSSG() const { return mixerVolumeSSG_; } private: int mixerVolumeMaster_; double mixerVolumeFM_, mixerVolumeSSG_; // Input // public: - void setFMEnvelopeTexts(std::vector texts); - std::vector getFMEnvelopeTexts() const; + void setFMEnvelopeTexts(const std::vector& texts) { fmEnvelopeTexts_ = texts; } + std::vector getFMEnvelopeTexts() const { return fmEnvelopeTexts_; } // Appearance // public: - void setPatternEditorHeaderFont(std::string font); - std::string getPatternEditorHeaderFont() const; - void setPatternEditorHeaderFontSize(int size); - int getPatternEditorHeaderFontSize() const; - void setPatternEditorRowsFont(std::string font); - std::string getPatternEditorRowsFont() const; - void setPatternEditorRowsFontSize(int size); - int getPatternEditorRowsFontSize() const; - void setOrderListHeaderFont(std::string font); - std::string getOrderListHeaderFont() const; - void setOrderListHeaderFontSize(int size); - int getOrderListHeaderFontSize() const; - void setOrderListRowsFont(std::string font); - std::string getOrderListRowsFont() const; - void setOrderListRowsFontSize(int size); - int getOrderListRowsFontSize() const; + void setPatternEditorHeaderFont(const std::string& font) { ptnHdFont_ = font; } + std::string getPatternEditorHeaderFont() const { return ptnHdFont_; } + void setPatternEditorHeaderFontSize(int size) { ptnHdFontSize_ = size; } + int getPatternEditorHeaderFontSize() const { return ptnHdFontSize_; } + void setPatternEditorRowsFont(const std::string& font) { ptnRowFont_ = font; } + std::string getPatternEditorRowsFont() const { return ptnRowFont_; } + void setPatternEditorRowsFontSize(int size) { ptnRowFontSize_ = size; } + int getPatternEditorRowsFontSize() const { return ptnRowFontSize_; } + void setOrderListHeaderFont(const std::string& font) { odrHdFont_ = font; } + std::string getOrderListHeaderFont() const { return odrHdFont_; } + void setOrderListHeaderFontSize(int size) { odrHdFontSize_ = size; } + int getOrderListHeaderFontSize() const { return odrHdFontSize_; } + void setOrderListRowsFont(const std::string& font) { odrRowFont_ = font; } + std::string getOrderListRowsFont() const { return odrRowFont_; } + void setOrderListRowsFontSize(int size) { odrRowFontSize_ = size; } + int getOrderListRowsFontSize() const { return odrRowFontSize_; } private: std::string ptnHdFont_, ptnRowFont_, odrHdFont_, odrRowFont_; int ptnHdFontSize_, ptnRowFontSize_, odrHdFontSize_, odrRowFontSize_; diff -Nru bambootracker-0.4.5+git20121209/BambooTracker/echo_buffer.hpp bambootracker-0.4.6/BambooTracker/echo_buffer.hpp --- bambootracker-0.4.5+git20121209/BambooTracker/echo_buffer.hpp 1970-01-01 00:00:00.000000000 +0000 +++ bambootracker-0.4.6/BambooTracker/echo_buffer.hpp 2021-02-11 14:37:17.000000000 +0000 @@ -0,0 +1,60 @@ +/* + * Copyright (C) 2021 Rerrah + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +#pragma once + +#include +#include "note.hpp" + +class EchoBuffer +{ + using DequeType = std::deque; +public: + using reference = DequeType::reference; + using const_reference = DequeType::const_reference; + using size_type = DequeType::size_type; + + reference at(size_type n) { return deque_.at(n); } + const_reference at(size_type n) const { return deque_.at(n); } + + reference operator[](size_type n) { return deque_[n]; } + const_reference operator[](size_type n) const { return deque_[n]; } + + reference latest() { return deque_.front(); } + const_reference latest() const { return deque_.front(); } + + size_type size() const noexcept { return deque_.size(); } + + void clear() noexcept { deque_.clear(); } + + void push(const Note& y) { + deque_.push_front(y); + if (MAX_ <= deque_.size()) deque_.pop_back(); + } + +private: + std::deque deque_; + static constexpr size_type MAX_ = 4; +}; diff -Nru bambootracker-0.4.5+git20121209/BambooTracker/gui/bookmark_manager_form.hpp bambootracker-0.4.6/BambooTracker/gui/bookmark_manager_form.hpp --- bambootracker-0.4.5+git20121209/BambooTracker/gui/bookmark_manager_form.hpp 2020-12-08 14:05:28.000000000 +0000 +++ bambootracker-0.4.6/BambooTracker/gui/bookmark_manager_form.hpp 2021-02-11 14:37:17.000000000 +0000 @@ -34,7 +34,7 @@ #include "bamboo_tracker.hpp" namespace Ui { - class BookmarkManagerForm; +class BookmarkManagerForm; } class BookmarkManagerForm : public QWidget diff -Nru bambootracker-0.4.5+git20121209/BambooTracker/gui/command/commands_qt.hpp bambootracker-0.4.6/BambooTracker/gui/command/commands_qt.hpp --- bambootracker-0.4.5+git20121209/BambooTracker/gui/command/commands_qt.hpp 2020-12-08 14:05:28.000000000 +0000 +++ bambootracker-0.4.6/BambooTracker/gui/command/commands_qt.hpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,37 +0,0 @@ -/* - * Copyright (C) 2018-2020 Rerrah - * - * Permission is hereby granted, free of charge, to any person - * obtaining a copy of this software and associated documentation - * files (the "Software"), to deal in the Software without - * restriction, including without limitation the rights to use, - * copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following - * conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES - * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT - * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, - * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR - * OTHER DEALINGS IN THE SOFTWARE. - */ - -#ifndef COMMANDS_QT_HPP -#define COMMANDS_QT_HPP - -/********** Instrument edit **********/ -#include "./instrument/add_instrument_qt_command.hpp" -#include "./instrument/remove_instrument_qt_command.hpp" -#include "./instrument/change_instrument_name_qt_command.hpp" -#include "./instrument/clone_instrument_qt_command.hpp" -#include "./instrument/deep_clone_instrument_qt_command.hpp" -#include "./instrument/swap_instruments_qt_command.hpp" - -#endif // COMMANDS_QT_HPP diff -Nru bambootracker-0.4.5+git20121209/BambooTracker/gui/command/instrument/add_instrument_qt_command.cpp bambootracker-0.4.6/BambooTracker/gui/command/instrument/add_instrument_qt_command.cpp --- bambootracker-0.4.5+git20121209/BambooTracker/gui/command/instrument/add_instrument_qt_command.cpp 2020-12-08 14:05:28.000000000 +0000 +++ bambootracker-0.4.6/BambooTracker/gui/command/instrument/add_instrument_qt_command.cpp 2021-02-11 14:37:17.000000000 +0000 @@ -28,9 +28,10 @@ #include "command/command_id.hpp" #include "instrument_command_qt_utils.hpp" -AddInstrumentQtCommand::AddInstrumentQtCommand(QListWidget *list, int num, QString name, InstrumentType type, - std::weak_ptr formMan, MainWindow* mainwin, - bool onlyUsed, bool preventFirstStore, QUndoCommand *parent) +AddInstrumentQtCommand::AddInstrumentQtCommand( + QListWidget *list, int num, const QString& name, InstrumentType type, + std::weak_ptr formMan, MainWindow* mainwin, + bool onlyUsed, bool preventFirstStore, QUndoCommand *parent) : QUndoCommand(parent), list_(list), num_(num), diff -Nru bambootracker-0.4.5+git20121209/BambooTracker/gui/command/instrument/add_instrument_qt_command.hpp bambootracker-0.4.6/BambooTracker/gui/command/instrument/add_instrument_qt_command.hpp --- bambootracker-0.4.5+git20121209/BambooTracker/gui/command/instrument/add_instrument_qt_command.hpp 2020-12-08 14:05:28.000000000 +0000 +++ bambootracker-0.4.6/BambooTracker/gui/command/instrument/add_instrument_qt_command.hpp 2021-02-11 14:37:17.000000000 +0000 @@ -36,16 +36,16 @@ enum class InstrumentType; -class AddInstrumentQtCommand : public QUndoCommand +class AddInstrumentQtCommand final : public QUndoCommand { public: - AddInstrumentQtCommand(QListWidget *list, int num, QString name, InstrumentType type, + AddInstrumentQtCommand(QListWidget *list, int num, const QString& name, InstrumentType type, std::weak_ptr formMan, MainWindow* mainwin, bool onlyUsed, bool preventFirstStore = false, QUndoCommand *parent = nullptr); - void undo() Q_DECL_OVERRIDE; - void redo() Q_DECL_OVERRIDE; - int id() const Q_DECL_OVERRIDE; + void undo() override; + void redo() override; + int id() const override; private: QListWidget *list_; diff -Nru bambootracker-0.4.5+git20121209/BambooTracker/gui/command/instrument/change_instrument_name_qt_command.cpp bambootracker-0.4.6/BambooTracker/gui/command/instrument/change_instrument_name_qt_command.cpp --- bambootracker-0.4.5+git20121209/BambooTracker/gui/command/instrument/change_instrument_name_qt_command.cpp 2020-12-08 14:05:28.000000000 +0000 +++ bambootracker-0.4.6/BambooTracker/gui/command/instrument/change_instrument_name_qt_command.cpp 2021-02-11 14:37:17.000000000 +0000 @@ -27,10 +27,9 @@ #include "command/command_id.hpp" #include "instrument_command_qt_utils.hpp" -ChangeInstrumentNameQtCommand::ChangeInstrumentNameQtCommand(QListWidget *list, int num, int row, - std::weak_ptr formMan, - QString oldName, QString newName, - QUndoCommand *parent) +ChangeInstrumentNameQtCommand::ChangeInstrumentNameQtCommand( + QListWidget *list, int num, int row, std::weak_ptr formMan, + const QString& oldName, const QString& newName, QUndoCommand *parent) : QUndoCommand(parent), list_(list), num_(num), diff -Nru bambootracker-0.4.5+git20121209/BambooTracker/gui/command/instrument/change_instrument_name_qt_command.hpp bambootracker-0.4.6/BambooTracker/gui/command/instrument/change_instrument_name_qt_command.hpp --- bambootracker-0.4.5+git20121209/BambooTracker/gui/command/instrument/change_instrument_name_qt_command.hpp 2020-12-08 14:05:28.000000000 +0000 +++ bambootracker-0.4.6/BambooTracker/gui/command/instrument/change_instrument_name_qt_command.hpp 2021-02-11 14:37:17.000000000 +0000 @@ -32,15 +32,16 @@ #include #include "gui/instrument_editor/instrument_form_manager.hpp" -class ChangeInstrumentNameQtCommand : public QUndoCommand +class ChangeInstrumentNameQtCommand final : public QUndoCommand { public: ChangeInstrumentNameQtCommand(QListWidget *list, int num, int row, std::weak_ptr formMan, - QString oldName, QString newName, QUndoCommand* parent = nullptr); - void undo() Q_DECL_OVERRIDE; - void redo() Q_DECL_OVERRIDE; - int id() const Q_DECL_OVERRIDE; + const QString& oldName, const QString& newName, + QUndoCommand* parent = nullptr); + void undo() override; + void redo() override; + int id() const override; private: QListWidget *list_; diff -Nru bambootracker-0.4.5+git20121209/BambooTracker/gui/command/instrument/clone_instrument_qt_command.cpp bambootracker-0.4.6/BambooTracker/gui/command/instrument/clone_instrument_qt_command.cpp --- bambootracker-0.4.5+git20121209/BambooTracker/gui/command/instrument/clone_instrument_qt_command.cpp 2020-12-08 14:05:28.000000000 +0000 +++ bambootracker-0.4.6/BambooTracker/gui/command/instrument/clone_instrument_qt_command.cpp 2021-02-11 14:37:17.000000000 +0000 @@ -28,9 +28,9 @@ #include "command/command_id.hpp" #include "instrument_command_qt_utils.hpp" -CloneInstrumentQtCommand::CloneInstrumentQtCommand(QListWidget *list, int num, InstrumentType type, QString name, - std::weak_ptr formMan, - QUndoCommand *parent) +CloneInstrumentQtCommand::CloneInstrumentQtCommand( + QListWidget *list, int num, InstrumentType type, const QString& name, + std::weak_ptr formMan, QUndoCommand *parent) : QUndoCommand(parent), list_(list), cloneNum_(num), diff -Nru bambootracker-0.4.5+git20121209/BambooTracker/gui/command/instrument/clone_instrument_qt_command.hpp bambootracker-0.4.6/BambooTracker/gui/command/instrument/clone_instrument_qt_command.hpp --- bambootracker-0.4.5+git20121209/BambooTracker/gui/command/instrument/clone_instrument_qt_command.hpp 2020-12-08 14:05:28.000000000 +0000 +++ bambootracker-0.4.6/BambooTracker/gui/command/instrument/clone_instrument_qt_command.hpp 2021-02-11 14:37:17.000000000 +0000 @@ -33,15 +33,15 @@ enum class InstrumentType; -class CloneInstrumentQtCommand : public QUndoCommand +class CloneInstrumentQtCommand final : public QUndoCommand { public: - CloneInstrumentQtCommand(QListWidget *list, int num, InstrumentType type, QString name, + CloneInstrumentQtCommand(QListWidget *list, int num, InstrumentType type, const QString& name, std::weak_ptr formMan, QUndoCommand* parent = nullptr); - void undo() Q_DECL_OVERRIDE; - void redo() Q_DECL_OVERRIDE; - int id() const Q_DECL_OVERRIDE; + void undo() override; + void redo() override; + int id() const override; private: QListWidget* list_; diff -Nru bambootracker-0.4.5+git20121209/BambooTracker/gui/command/instrument/deep_clone_instrument_qt_command.cpp bambootracker-0.4.6/BambooTracker/gui/command/instrument/deep_clone_instrument_qt_command.cpp --- bambootracker-0.4.5+git20121209/BambooTracker/gui/command/instrument/deep_clone_instrument_qt_command.cpp 2020-12-08 14:05:28.000000000 +0000 +++ bambootracker-0.4.6/BambooTracker/gui/command/instrument/deep_clone_instrument_qt_command.cpp 2021-02-11 14:37:17.000000000 +0000 @@ -28,9 +28,10 @@ #include "command/command_id.hpp" #include "instrument_command_qt_utils.hpp" -DeepCloneInstrumentQtCommand::DeepCloneInstrumentQtCommand(QListWidget *list, int num, InstrumentType type, QString name, - std::weak_ptr formMan, - MainWindow* mainwin, bool onlyUsed, QUndoCommand *parent) +DeepCloneInstrumentQtCommand::DeepCloneInstrumentQtCommand( + QListWidget *list, int num, InstrumentType type, const QString& name, + std::weak_ptr formMan, MainWindow* mainwin, bool onlyUsed, + QUndoCommand *parent) : QUndoCommand(parent), list_(list), cloneNum_(num), diff -Nru bambootracker-0.4.5+git20121209/BambooTracker/gui/command/instrument/deep_clone_instrument_qt_command.hpp bambootracker-0.4.6/BambooTracker/gui/command/instrument/deep_clone_instrument_qt_command.hpp --- bambootracker-0.4.5+git20121209/BambooTracker/gui/command/instrument/deep_clone_instrument_qt_command.hpp 2020-12-08 14:05:28.000000000 +0000 +++ bambootracker-0.4.6/BambooTracker/gui/command/instrument/deep_clone_instrument_qt_command.hpp 2021-02-11 14:37:17.000000000 +0000 @@ -34,16 +34,16 @@ enum class InstrumentType; -class DeepCloneInstrumentQtCommand : public QUndoCommand +class DeepCloneInstrumentQtCommand final : public QUndoCommand { public: - DeepCloneInstrumentQtCommand(QListWidget *list, int num, InstrumentType type, QString name, - std::weak_ptr formMan, MainWindow* mainwin, bool onlyUsed, - QUndoCommand* parent = nullptr); + DeepCloneInstrumentQtCommand(QListWidget *list, int num, InstrumentType type, const QString& name, + std::weak_ptr formMan, MainWindow* mainwin, + bool onlyUsed, QUndoCommand* parent = nullptr); - void undo() Q_DECL_OVERRIDE; - void redo() Q_DECL_OVERRIDE; - int id() const Q_DECL_OVERRIDE; + void undo() override; + void redo() override; + int id() const override; private: QListWidget* list_; diff -Nru bambootracker-0.4.5+git20121209/BambooTracker/gui/command/instrument/instrument_command_qt_utils.cpp bambootracker-0.4.6/BambooTracker/gui/command/instrument/instrument_command_qt_utils.cpp --- bambootracker-0.4.5+git20121209/BambooTracker/gui/command/instrument/instrument_command_qt_utils.cpp 2020-12-08 14:05:28.000000000 +0000 +++ bambootracker-0.4.6/BambooTracker/gui/command/instrument/instrument_command_qt_utils.cpp 2021-02-11 14:37:17.000000000 +0000 @@ -29,6 +29,8 @@ #include "instrument.hpp" #include "enum_hash.hpp" +namespace gui_command_utils +{ namespace { const std::unordered_map ICON_SRC = { @@ -39,9 +41,7 @@ }; } -namespace gui_command_utils -{ -QListWidgetItem* createInstrumentListItem(int num, InstrumentType type, QString name) +QListWidgetItem* createInstrumentListItem(int num, InstrumentType type, const QString& name) { QListWidgetItem *item = new QListWidgetItem(QIcon(ICON_SRC.at(type)), makeInstrumentListText(num, name)); item->setSizeHint(QSize(130, 17)); diff -Nru bambootracker-0.4.5+git20121209/BambooTracker/gui/command/instrument/instrument_command_qt_utils.hpp bambootracker-0.4.6/BambooTracker/gui/command/instrument/instrument_command_qt_utils.hpp --- bambootracker-0.4.5+git20121209/BambooTracker/gui/command/instrument/instrument_command_qt_utils.hpp 2020-12-08 14:05:28.000000000 +0000 +++ bambootracker-0.4.6/BambooTracker/gui/command/instrument/instrument_command_qt_utils.hpp 2021-02-11 14:37:17.000000000 +0000 @@ -33,12 +33,12 @@ namespace gui_command_utils { -inline QString makeInstrumentListText(int num, QString name) +inline QString makeInstrumentListText(int num, const QString& name) { return QString("%1: %2").arg(num, 2, 16, QChar('0')).toUpper().arg(name); } -QListWidgetItem* createInstrumentListItem(int num, InstrumentType type, QString name); +QListWidgetItem* createInstrumentListItem(int num, InstrumentType type, const QString& name); } #endif // INSTRUMENT_COMMAND_QT_UTILS_HPP diff -Nru bambootracker-0.4.5+git20121209/BambooTracker/gui/command/instrument/instrument_commands_qt.hpp bambootracker-0.4.6/BambooTracker/gui/command/instrument/instrument_commands_qt.hpp --- bambootracker-0.4.5+git20121209/BambooTracker/gui/command/instrument/instrument_commands_qt.hpp 1970-01-01 00:00:00.000000000 +0000 +++ bambootracker-0.4.6/BambooTracker/gui/command/instrument/instrument_commands_qt.hpp 2021-02-11 14:37:17.000000000 +0000 @@ -0,0 +1,37 @@ +/* + * Copyright (C) 2018-2020 Rerrah + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +#ifndef INSTRUMENT_COMMANDS_QT_HPP +#define INSTRUMENT_COMMANDS_QT_HPP + +/********** Instrument edit **********/ +#include "add_instrument_qt_command.hpp" +#include "remove_instrument_qt_command.hpp" +#include "change_instrument_name_qt_command.hpp" +#include "clone_instrument_qt_command.hpp" +#include "deep_clone_instrument_qt_command.hpp" +#include "swap_instruments_qt_command.hpp" + +#endif // INSTRUMENT_COMMANDS_QT_HPP diff -Nru bambootracker-0.4.5+git20121209/BambooTracker/gui/command/instrument/remove_instrument_qt_command.cpp bambootracker-0.4.6/BambooTracker/gui/command/instrument/remove_instrument_qt_command.cpp --- bambootracker-0.4.5+git20121209/BambooTracker/gui/command/instrument/remove_instrument_qt_command.cpp 2020-12-08 14:05:28.000000000 +0000 +++ bambootracker-0.4.6/BambooTracker/gui/command/instrument/remove_instrument_qt_command.cpp 2021-02-11 14:37:17.000000000 +0000 @@ -29,9 +29,10 @@ #include "command/command_id.hpp" #include "instrument_command_qt_utils.hpp" -RemoveInstrumentQtCommand::RemoveInstrumentQtCommand(QListWidget *list, int num, int row, QString name, InstrumentType type, - std::weak_ptr formMan, - MainWindow* mainwin, bool updateRequested, QUndoCommand *parent) +RemoveInstrumentQtCommand::RemoveInstrumentQtCommand( + QListWidget *list, int num, int row, const QString& name, InstrumentType type, + std::weak_ptr formMan, + MainWindow* mainwin, bool updateRequested, QUndoCommand *parent) : QUndoCommand(parent), list_(list), num_(num), diff -Nru bambootracker-0.4.5+git20121209/BambooTracker/gui/command/instrument/remove_instrument_qt_command.hpp bambootracker-0.4.6/BambooTracker/gui/command/instrument/remove_instrument_qt_command.hpp --- bambootracker-0.4.5+git20121209/BambooTracker/gui/command/instrument/remove_instrument_qt_command.hpp 2020-12-08 14:05:28.000000000 +0000 +++ bambootracker-0.4.6/BambooTracker/gui/command/instrument/remove_instrument_qt_command.hpp 2021-02-11 14:37:17.000000000 +0000 @@ -36,15 +36,15 @@ enum class InstrumentType; -class RemoveInstrumentQtCommand : public QUndoCommand +class RemoveInstrumentQtCommand final : public QUndoCommand { public: - RemoveInstrumentQtCommand(QListWidget *list, int num, int row, QString name, InstrumentType type, - std::weak_ptr formMan, + RemoveInstrumentQtCommand(QListWidget *list, int num, int row, const QString& name, + InstrumentType type, std::weak_ptr formMan, MainWindow* mainwin, bool updateRequested, QUndoCommand *parent = nullptr); - void undo() Q_DECL_OVERRIDE; - void redo() Q_DECL_OVERRIDE; - int id() const Q_DECL_OVERRIDE; + void undo() override; + void redo() override; + int id() const override; private: QListWidget *list_; diff -Nru bambootracker-0.4.5+git20121209/BambooTracker/gui/command/instrument/swap_instruments_qt_command.cpp bambootracker-0.4.6/BambooTracker/gui/command/instrument/swap_instruments_qt_command.cpp --- bambootracker-0.4.5+git20121209/BambooTracker/gui/command/instrument/swap_instruments_qt_command.cpp 2020-12-08 14:05:28.000000000 +0000 +++ bambootracker-0.4.6/BambooTracker/gui/command/instrument/swap_instruments_qt_command.cpp 2021-02-11 14:37:17.000000000 +0000 @@ -30,11 +30,10 @@ #include "gui/pattern_editor/pattern_editor.hpp" #include "instrument_command_qt_utils.hpp" -SwapInstrumentsQtCommand::SwapInstrumentsQtCommand(QListWidget* list, int inst1Row, int inst2Row, - QString inst1Name, QString inst2Name, - std::weak_ptr formMan, - PatternEditor* pattern, - QUndoCommand* parent) +SwapInstrumentsQtCommand::SwapInstrumentsQtCommand( + QListWidget* list, int inst1Row, int inst2Row, const QString& inst1Name, + const QString& inst2Name, std::weak_ptr formMan, + PatternEditor* pattern, QUndoCommand* parent) : QUndoCommand(parent), list_(list), ptn_(pattern), diff -Nru bambootracker-0.4.5+git20121209/BambooTracker/gui/command/instrument/swap_instruments_qt_command.hpp bambootracker-0.4.6/BambooTracker/gui/command/instrument/swap_instruments_qt_command.hpp --- bambootracker-0.4.5+git20121209/BambooTracker/gui/command/instrument/swap_instruments_qt_command.hpp 2020-12-08 14:05:28.000000000 +0000 +++ bambootracker-0.4.6/BambooTracker/gui/command/instrument/swap_instruments_qt_command.hpp 2021-02-11 14:37:17.000000000 +0000 @@ -34,17 +34,16 @@ class PatternEditor; -class SwapInstrumentsQtCommand : public QUndoCommand +class SwapInstrumentsQtCommand final : public QUndoCommand { public: SwapInstrumentsQtCommand(QListWidget *list, int inst1Row, int inst2Row, - QString inst1Name, QString inst2Name, + const QString& inst1Name, const QString& inst2Name, std::weak_ptr formMan, - PatternEditor* pattern, - QUndoCommand* parent = nullptr); - void undo() Q_DECL_OVERRIDE; - void redo() Q_DECL_OVERRIDE; - int id() const Q_DECL_OVERRIDE; + PatternEditor* pattern, QUndoCommand* parent = nullptr); + void undo() override; + void redo() override; + int id() const override; private: QListWidget* list_; diff -Nru bambootracker-0.4.5+git20121209/BambooTracker/gui/command/order/clone_order_qt_command.cpp bambootracker-0.4.6/BambooTracker/gui/command/order/clone_order_qt_command.cpp --- bambootracker-0.4.5+git20121209/BambooTracker/gui/command/order/clone_order_qt_command.cpp 2020-12-08 14:05:28.000000000 +0000 +++ bambootracker-0.4.6/BambooTracker/gui/command/order/clone_order_qt_command.cpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,50 +0,0 @@ -/* - * Copyright (C) 2018-2019 Rerrah - * - * Permission is hereby granted, free of charge, to any person - * obtaining a copy of this software and associated documentation - * files (the "Software"), to deal in the Software without - * restriction, including without limitation the rights to use, - * copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following - * conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES - * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT - * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, - * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR - * OTHER DEALINGS IN THE SOFTWARE. - */ - -#include "clone_order_qt_command.hpp" -#include "command/command_id.hpp" - -CloneOrderQtCommand::CloneOrderQtCommand(OrderListPanel* panel, QUndoCommand* parent) - : QUndoCommand(parent), - panel_(panel) -{ -} - -void CloneOrderQtCommand::redo() -{ - panel_->onOrderEdited(); - panel_->redrawByPatternChanged(true); -} - -void CloneOrderQtCommand::undo() -{ - panel_->onOrderEdited(); - panel_->redrawByPatternChanged(true); -} - -int CloneOrderQtCommand::id() const -{ - return CommandId::CloneOrder; -} diff -Nru bambootracker-0.4.5+git20121209/BambooTracker/gui/command/order/clone_order_qt_command.hpp bambootracker-0.4.6/BambooTracker/gui/command/order/clone_order_qt_command.hpp --- bambootracker-0.4.5+git20121209/BambooTracker/gui/command/order/clone_order_qt_command.hpp 2020-12-08 14:05:28.000000000 +0000 +++ bambootracker-0.4.6/BambooTracker/gui/command/order/clone_order_qt_command.hpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,44 +0,0 @@ -/* - * Copyright (C) 2018 Rerrah - * - * Permission is hereby granted, free of charge, to any person - * obtaining a copy of this software and associated documentation - * files (the "Software"), to deal in the Software without - * restriction, including without limitation the rights to use, - * copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following - * conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES - * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT - * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, - * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR - * OTHER DEALINGS IN THE SOFTWARE. - */ - -#ifndef CLONE_ORDER_QT_COMMAND_HPP -#define CLONE_ORDER_QT_COMMAND_HPP - -#include -#include "gui/order_list_editor/order_list_panel.hpp" - -class CloneOrderQtCommand : public QUndoCommand -{ -public: - CloneOrderQtCommand(OrderListPanel* panel, QUndoCommand* parent = nullptr); - void redo() Q_DECL_OVERRIDE; - void undo() Q_DECL_OVERRIDE; - int id() const Q_DECL_OVERRIDE; - -private: - OrderListPanel* panel_; -}; - -#endif // CLONE_ORDER_QT_COMMAND_HPP diff -Nru bambootracker-0.4.5+git20121209/BambooTracker/gui/command/order/clone_patterns_qt_command.cpp bambootracker-0.4.6/BambooTracker/gui/command/order/clone_patterns_qt_command.cpp --- bambootracker-0.4.5+git20121209/BambooTracker/gui/command/order/clone_patterns_qt_command.cpp 2020-12-08 14:05:28.000000000 +0000 +++ bambootracker-0.4.6/BambooTracker/gui/command/order/clone_patterns_qt_command.cpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,50 +0,0 @@ -/* - * Copyright (C) 2018-2019 Rerrah - * - * Permission is hereby granted, free of charge, to any person - * obtaining a copy of this software and associated documentation - * files (the "Software"), to deal in the Software without - * restriction, including without limitation the rights to use, - * copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following - * conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES - * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT - * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, - * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR - * OTHER DEALINGS IN THE SOFTWARE. - */ - -#include "clone_patterns_qt_command.hpp" -#include "command/command_id.hpp" - -ClonePatternsQtCommand::ClonePatternsQtCommand(OrderListPanel* panel, QUndoCommand* parent) - : QUndoCommand(parent), - panel_(panel) -{ -} - -void ClonePatternsQtCommand::redo() -{ - panel_->onOrderEdited(); - panel_->redrawByPatternChanged(); -} - -void ClonePatternsQtCommand::undo() -{ - panel_->onOrderEdited(); - panel_->redrawByPatternChanged(); -} - -int ClonePatternsQtCommand::id() const -{ - return CommandId::ClonePatterns; -} diff -Nru bambootracker-0.4.5+git20121209/BambooTracker/gui/command/order/clone_patterns_qt_command.hpp bambootracker-0.4.6/BambooTracker/gui/command/order/clone_patterns_qt_command.hpp --- bambootracker-0.4.5+git20121209/BambooTracker/gui/command/order/clone_patterns_qt_command.hpp 2020-12-08 14:05:28.000000000 +0000 +++ bambootracker-0.4.6/BambooTracker/gui/command/order/clone_patterns_qt_command.hpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,44 +0,0 @@ -/* - * Copyright (C) 2018 Rerrah - * - * Permission is hereby granted, free of charge, to any person - * obtaining a copy of this software and associated documentation - * files (the "Software"), to deal in the Software without - * restriction, including without limitation the rights to use, - * copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following - * conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES - * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT - * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, - * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR - * OTHER DEALINGS IN THE SOFTWARE. - */ - -#ifndef CLONE_PATTERNS_QT_COMMAND_HPP -#define CLONE_PATTERNS_QT_COMMAND_HPP - -#include -#include "gui/order_list_editor/order_list_panel.hpp" - -class ClonePatternsQtCommand : public QUndoCommand -{ -public: - ClonePatternsQtCommand(OrderListPanel* panel, QUndoCommand* parent = nullptr); - void redo() Q_DECL_OVERRIDE; - void undo() Q_DECL_OVERRIDE; - int id() const Q_DECL_OVERRIDE; - -private: - OrderListPanel* panel_; -}; - -#endif // CLONE_PATTERNS_QT_COMMAND_HPP diff -Nru bambootracker-0.4.5+git20121209/BambooTracker/gui/command/order/delete_order_qt_command.cpp bambootracker-0.4.6/BambooTracker/gui/command/order/delete_order_qt_command.cpp --- bambootracker-0.4.5+git20121209/BambooTracker/gui/command/order/delete_order_qt_command.cpp 2020-12-08 14:05:28.000000000 +0000 +++ bambootracker-0.4.6/BambooTracker/gui/command/order/delete_order_qt_command.cpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,50 +0,0 @@ -/* - * Copyright (C) 2018-2020 Rerrah - * - * Permission is hereby granted, free of charge, to any person - * obtaining a copy of this software and associated documentation - * files (the "Software"), to deal in the Software without - * restriction, including without limitation the rights to use, - * copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following - * conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES - * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT - * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, - * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR - * OTHER DEALINGS IN THE SOFTWARE. - */ - -#include "delete_order_qt_command.hpp" -#include "command/command_id.hpp" - -DeleteOrderQtCommand::DeleteOrderQtCommand(OrderListPanel* panel, QUndoCommand* parent) - : QUndoCommand(parent), - panel_(panel) -{ -} - -void DeleteOrderQtCommand::redo() -{ - panel_->onOrderEdited(); - panel_->redrawByPatternChanged(true); -} - -void DeleteOrderQtCommand::undo() -{ - panel_->onOrderEdited(); - panel_->redrawByPatternChanged(true); -} - -int DeleteOrderQtCommand::id() const -{ - return CommandId::DeleteOrder; -} diff -Nru bambootracker-0.4.5+git20121209/BambooTracker/gui/command/order/delete_order_qt_command.hpp bambootracker-0.4.6/BambooTracker/gui/command/order/delete_order_qt_command.hpp --- bambootracker-0.4.5+git20121209/BambooTracker/gui/command/order/delete_order_qt_command.hpp 2020-12-08 14:05:28.000000000 +0000 +++ bambootracker-0.4.6/BambooTracker/gui/command/order/delete_order_qt_command.hpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,44 +0,0 @@ -/* - * Copyright (C) 2018 Rerrah - * - * Permission is hereby granted, free of charge, to any person - * obtaining a copy of this software and associated documentation - * files (the "Software"), to deal in the Software without - * restriction, including without limitation the rights to use, - * copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following - * conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES - * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT - * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, - * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR - * OTHER DEALINGS IN THE SOFTWARE. - */ - -#ifndef DELETE_ORDER_QT_COMMAND_HPP -#define DELETE_ORDER_QT_COMMAND_HPP - -#include -#include "gui/order_list_editor/order_list_panel.hpp" - -class DeleteOrderQtCommand : public QUndoCommand -{ -public: - DeleteOrderQtCommand(OrderListPanel* panel, QUndoCommand* parent = nullptr); - void redo() Q_DECL_OVERRIDE; - void undo() Q_DECL_OVERRIDE; - int id() const Q_DECL_OVERRIDE; - -private: - OrderListPanel* panel_; -}; - -#endif // DELETE_ORDER_QT_COMMAND_HPP diff -Nru bambootracker-0.4.5+git20121209/BambooTracker/gui/command/order/duplicate_order_qt_command.cpp bambootracker-0.4.6/BambooTracker/gui/command/order/duplicate_order_qt_command.cpp --- bambootracker-0.4.5+git20121209/BambooTracker/gui/command/order/duplicate_order_qt_command.cpp 2020-12-08 14:05:28.000000000 +0000 +++ bambootracker-0.4.6/BambooTracker/gui/command/order/duplicate_order_qt_command.cpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,50 +0,0 @@ -/* - * Copyright (C) 2018-2019 Rerrah - * - * Permission is hereby granted, free of charge, to any person - * obtaining a copy of this software and associated documentation - * files (the "Software"), to deal in the Software without - * restriction, including without limitation the rights to use, - * copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following - * conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES - * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT - * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, - * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR - * OTHER DEALINGS IN THE SOFTWARE. - */ - -#include "duplicate_order_qt_command.hpp" -#include "command/command_id.hpp" - -DuplicateOrderQtCommand::DuplicateOrderQtCommand(OrderListPanel* panel, QUndoCommand* parent) - : QUndoCommand(parent), - panel_(panel) -{ -} - -void DuplicateOrderQtCommand::redo() -{ - panel_->onOrderEdited(); - panel_->redrawByPatternChanged(true); -} - -void DuplicateOrderQtCommand::undo() -{ - panel_->onOrderEdited(); - panel_->redrawByPatternChanged(true); -} - -int DuplicateOrderQtCommand::id() const -{ - return CommandId::DuplicateOrder; -} diff -Nru bambootracker-0.4.5+git20121209/BambooTracker/gui/command/order/duplicate_order_qt_command.hpp bambootracker-0.4.6/BambooTracker/gui/command/order/duplicate_order_qt_command.hpp --- bambootracker-0.4.5+git20121209/BambooTracker/gui/command/order/duplicate_order_qt_command.hpp 2020-12-08 14:05:28.000000000 +0000 +++ bambootracker-0.4.6/BambooTracker/gui/command/order/duplicate_order_qt_command.hpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,44 +0,0 @@ -/* - * Copyright (C) 2018 Rerrah - * - * Permission is hereby granted, free of charge, to any person - * obtaining a copy of this software and associated documentation - * files (the "Software"), to deal in the Software without - * restriction, including without limitation the rights to use, - * copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following - * conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES - * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT - * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, - * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR - * OTHER DEALINGS IN THE SOFTWARE. - */ - -#ifndef DUPLICATE_ORDER_QT_COMMAND_HPP -#define DUPLICATE_ORDER_QT_COMMAND_HPP - -#include -#include "gui/order_list_editor/order_list_panel.hpp" - -class DuplicateOrderQtCommand : public QUndoCommand -{ -public: - DuplicateOrderQtCommand(OrderListPanel* panel, QUndoCommand* parent = nullptr); - void redo() Q_DECL_OVERRIDE; - void undo() Q_DECL_OVERRIDE; - int id() const Q_DECL_OVERRIDE; - -private: - OrderListPanel* panel_; -}; - -#endif // DUPLICATE_ORDER_QT_COMMAND_HPP diff -Nru bambootracker-0.4.5+git20121209/BambooTracker/gui/command/order/insert_order_below_qt_command.cpp bambootracker-0.4.6/BambooTracker/gui/command/order/insert_order_below_qt_command.cpp --- bambootracker-0.4.5+git20121209/BambooTracker/gui/command/order/insert_order_below_qt_command.cpp 2020-12-08 14:05:28.000000000 +0000 +++ bambootracker-0.4.6/BambooTracker/gui/command/order/insert_order_below_qt_command.cpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,50 +0,0 @@ -/* - * Copyright (C) 2018-2019 Rerrah - * - * Permission is hereby granted, free of charge, to any person - * obtaining a copy of this software and associated documentation - * files (the "Software"), to deal in the Software without - * restriction, including without limitation the rights to use, - * copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following - * conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES - * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT - * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, - * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR - * OTHER DEALINGS IN THE SOFTWARE. - */ - -#include "insert_order_below_qt_command.hpp" -#include "command/command_id.hpp" - -InsertOrderBelowQtCommand::InsertOrderBelowQtCommand(OrderListPanel* panel, QUndoCommand* parent) - : QUndoCommand(parent), - panel_(panel) -{ -} - -void InsertOrderBelowQtCommand::redo() -{ - panel_->onOrderEdited(); - panel_->redrawByPatternChanged(true); -} - -void InsertOrderBelowQtCommand::undo() -{ - panel_->onOrderEdited(); - panel_->redrawByPatternChanged(true); -} - -int InsertOrderBelowQtCommand::id() const -{ - return CommandId::InsertOrderBelow; -} diff -Nru bambootracker-0.4.5+git20121209/BambooTracker/gui/command/order/insert_order_below_qt_command.hpp bambootracker-0.4.6/BambooTracker/gui/command/order/insert_order_below_qt_command.hpp --- bambootracker-0.4.5+git20121209/BambooTracker/gui/command/order/insert_order_below_qt_command.hpp 2020-12-08 14:05:28.000000000 +0000 +++ bambootracker-0.4.6/BambooTracker/gui/command/order/insert_order_below_qt_command.hpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,44 +0,0 @@ -/* - * Copyright (C) 2018 Rerrah - * - * Permission is hereby granted, free of charge, to any person - * obtaining a copy of this software and associated documentation - * files (the "Software"), to deal in the Software without - * restriction, including without limitation the rights to use, - * copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following - * conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES - * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT - * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, - * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR - * OTHER DEALINGS IN THE SOFTWARE. - */ - -#ifndef INSERT_ORDER_BELOW_QT_COMMAND_HPP -#define INSERT_ORDER_BELOW_QT_COMMAND_HPP - -#include -#include "gui/order_list_editor/order_list_panel.hpp" - -class InsertOrderBelowQtCommand : public QUndoCommand -{ -public: - InsertOrderBelowQtCommand(OrderListPanel* panel, QUndoCommand* parent = nullptr); - void redo() Q_DECL_OVERRIDE; - void undo() Q_DECL_OVERRIDE; - int id() const Q_DECL_OVERRIDE; - -private: - OrderListPanel* panel_; -}; - -#endif // INSERT_ORDER_BELOW_QT_COMMAND_HPP diff -Nru bambootracker-0.4.5+git20121209/BambooTracker/gui/command/order/move_order_qt_command.cpp bambootracker-0.4.6/BambooTracker/gui/command/order/move_order_qt_command.cpp --- bambootracker-0.4.5+git20121209/BambooTracker/gui/command/order/move_order_qt_command.cpp 2020-12-08 14:05:28.000000000 +0000 +++ bambootracker-0.4.6/BambooTracker/gui/command/order/move_order_qt_command.cpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,50 +0,0 @@ -/* - * Copyright (C) 2018-2019 Rerrah - * - * Permission is hereby granted, free of charge, to any person - * obtaining a copy of this software and associated documentation - * files (the "Software"), to deal in the Software without - * restriction, including without limitation the rights to use, - * copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following - * conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES - * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT - * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, - * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR - * OTHER DEALINGS IN THE SOFTWARE. - */ - -#include "move_order_qt_command.hpp" -#include "command/command_id.hpp" - -MoveOrderQtCommand::MoveOrderQtCommand(OrderListPanel* panel, QUndoCommand* parent) - : QUndoCommand(parent), - panel_(panel) -{ -} - -void MoveOrderQtCommand::redo() -{ - panel_->onOrderEdited(); - panel_->redrawByPatternChanged(); -} - -void MoveOrderQtCommand::undo() -{ - panel_->onOrderEdited(); - panel_->redrawByPatternChanged(); -} - -int MoveOrderQtCommand::id() const -{ - return CommandId::MoveOrder; -} diff -Nru bambootracker-0.4.5+git20121209/BambooTracker/gui/command/order/move_order_qt_command.hpp bambootracker-0.4.6/BambooTracker/gui/command/order/move_order_qt_command.hpp --- bambootracker-0.4.5+git20121209/BambooTracker/gui/command/order/move_order_qt_command.hpp 2020-12-08 14:05:28.000000000 +0000 +++ bambootracker-0.4.6/BambooTracker/gui/command/order/move_order_qt_command.hpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,45 +0,0 @@ -/* - * Copyright (C) 2018 Rerrah - * - * Permission is hereby granted, free of charge, to any person - * obtaining a copy of this software and associated documentation - * files (the "Software"), to deal in the Software without - * restriction, including without limitation the rights to use, - * copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following - * conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES - * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT - * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, - * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR - * OTHER DEALINGS IN THE SOFTWARE. - */ - -#ifndef MOVE_ORDER_QT_COMMAND_HPP -#define MOVE_ORDER_QT_COMMAND_HPP - -#include -#include "gui/order_list_editor/order_list_panel.hpp" - -class MoveOrderQtCommand : public QUndoCommand -{ -public: - MoveOrderQtCommand(OrderListPanel* panel, QUndoCommand* parent = nullptr); - void redo() Q_DECL_OVERRIDE; - void undo() Q_DECL_OVERRIDE; - int id() const Q_DECL_OVERRIDE; - -private: - OrderListPanel* panel_; - -}; - -#endif // MOVE_ORDER_QT_COMMAND_HPP diff -Nru bambootracker-0.4.5+git20121209/BambooTracker/gui/command/order/order_commands.hpp bambootracker-0.4.6/BambooTracker/gui/command/order/order_commands.hpp --- bambootracker-0.4.5+git20121209/BambooTracker/gui/command/order/order_commands.hpp 2020-12-08 14:05:28.000000000 +0000 +++ bambootracker-0.4.6/BambooTracker/gui/command/order/order_commands.hpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,38 +0,0 @@ -/* - * Copyright (C) 2018 Rerrah - * - * Permission is hereby granted, free of charge, to any person - * obtaining a copy of this software and associated documentation - * files (the "Software"), to deal in the Software without - * restriction, including without limitation the rights to use, - * copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following - * conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES - * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT - * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, - * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR - * OTHER DEALINGS IN THE SOFTWARE. - */ - -#ifndef ORDER_COMMANDS_HPP -#define ORDER_COMMANDS_HPP - -#include "set_pattern_to_order_qt_command.hpp" -#include "insert_order_below_qt_command.hpp" -#include "delete_order_qt_command.hpp" -#include "paste_copied_data_to_order_qt_command.hpp" -#include "duplicate_order_qt_command.hpp" -#include "move_order_qt_command.hpp" -#include "clone_patterns_qt_command.hpp" -#include "clone_order_qt_command.hpp" - -#endif // ORDER_COMMANDS_HPP diff -Nru bambootracker-0.4.5+git20121209/BambooTracker/gui/command/order/order_commands_qt.hpp bambootracker-0.4.6/BambooTracker/gui/command/order/order_commands_qt.hpp --- bambootracker-0.4.5+git20121209/BambooTracker/gui/command/order/order_commands_qt.hpp 1970-01-01 00:00:00.000000000 +0000 +++ bambootracker-0.4.6/BambooTracker/gui/command/order/order_commands_qt.hpp 2021-02-11 14:37:17.000000000 +0000 @@ -0,0 +1,40 @@ +/* + * Copyright (C) 2018-2020 Rerrah + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +#ifndef ORDER_COMMANDS_QT_HPP +#define ORDER_COMMANDS_QT_HPP + +#include "order_list_common_qt_command.hpp" + +using CloneOrderQtCommand = gui_command_impl::OrderListCommonQtCommandRedrawAll; +using ClonePatternsQtCommand = gui_command_impl::OrderListCommonQtCommandRedrawText; +using DeleteOrderQtCommand = gui_command_impl::OrderListCommonQtCommandRedrawAll; +using DuplicateOrderQtCommand = gui_command_impl::OrderListCommonQtCommandRedrawAll; +using InsertOrderBelowQtCommand = gui_command_impl::OrderListCommonQtCommandRedrawAll; +using MoveOrderQtCommand = gui_command_impl::OrderListCommonQtCommandRedrawText; +using PasteCopiedDataToOrderQtCommand = gui_command_impl::OrderListCommonQtCommandRedrawText; +using SetPatternToOrderQtCommand = gui_command_impl::OrderListEntryQtCommandRedrawText; + +#endif // ORDER_COMMANDS_QT_HPP diff -Nru bambootracker-0.4.5+git20121209/BambooTracker/gui/command/order/order_list_common_qt_command.cpp bambootracker-0.4.6/BambooTracker/gui/command/order/order_list_common_qt_command.cpp --- bambootracker-0.4.5+git20121209/BambooTracker/gui/command/order/order_list_common_qt_command.cpp 1970-01-01 00:00:00.000000000 +0000 +++ bambootracker-0.4.6/BambooTracker/gui/command/order/order_list_common_qt_command.cpp 2021-02-11 14:37:17.000000000 +0000 @@ -0,0 +1,83 @@ +/* + * Copyright (C) 2020 Rerrah + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +#include "order_list_common_qt_command.hpp" +#include "gui/order_list_editor/order_list_panel.hpp" + +namespace gui_command_impl +{ +OrderListCommonQtCommand::OrderListCommonQtCommand( + CommandId id, OrderListPanel* panel, bool orderLengthChanged, QUndoCommand* parent) + : QUndoCommand(parent), panel_(panel), id_(id), orderLenChanged_(orderLengthChanged) +{ +} + +void OrderListCommonQtCommand::redo() +{ + panel_->onOrderEdited(); + panel_->redrawByPatternChanged(orderLenChanged_); +} + +void OrderListCommonQtCommand::undo() +{ + panel_->onOrderEdited(); + panel_->redrawByPatternChanged(orderLenChanged_); +} + +int OrderListCommonQtCommand::id() const +{ + return id_; +} + +OrderListEntryQtCommand::OrderListEntryQtCommand( + CommandId id, OrderListPanel* panel, bool redrawAll, const OrderPosition& pos, + bool secondEntry, QUndoCommand* parent) + : OrderListCommonQtCommand(id, panel, redrawAll, parent), + pos_(pos), + isSecondEntry_(secondEntry) +{ +} + +void OrderListEntryQtCommand::undo() +{ + OrderListCommonQtCommand::undo(); + panel_->resetEntryCount(); +} + +bool OrderListEntryQtCommand::mergeWith(const QUndoCommand* other) +{ + if (other->id() == id() && !isSecondEntry_) { + auto com = dynamic_cast(other); + if (com->pos_ == pos_ && com->isSecondEntry_) { + isSecondEntry_ = true; + redo(); + return true; + } + } + + isSecondEntry_ = true; + return false; +} +} diff -Nru bambootracker-0.4.5+git20121209/BambooTracker/gui/command/order/order_list_common_qt_command.hpp bambootracker-0.4.6/BambooTracker/gui/command/order/order_list_common_qt_command.hpp --- bambootracker-0.4.5+git20121209/BambooTracker/gui/command/order/order_list_common_qt_command.hpp 1970-01-01 00:00:00.000000000 +0000 +++ bambootracker-0.4.6/BambooTracker/gui/command/order/order_list_common_qt_command.hpp 2021-02-11 14:37:17.000000000 +0000 @@ -0,0 +1,99 @@ +/* + * Copyright (C) 2020 Rerrah + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +#ifndef ORDER_LIST_COMMON_QT_COMMAND_HPP +#define ORDER_LIST_COMMON_QT_COMMAND_HPP + +#include +#include "command/command_id.hpp" +#include "gui/order_list_editor/order_position.hpp" + +class OrderListPanel; + +namespace gui_command_impl +{ +class OrderListCommonQtCommand : public QUndoCommand +{ +public: + virtual void redo() override; + virtual void undo() override; + int id() const override final; + +protected: + OrderListPanel* panel_; + OrderListCommonQtCommand(CommandId id, OrderListPanel* panel, + bool redrawAll, QUndoCommand* parent); + +private: + CommandId id_; + bool orderLenChanged_; +}; + +template +class OrderListCommonQtCommandRedraw final : public OrderListCommonQtCommand +{ +public: + OrderListCommonQtCommandRedraw(OrderListPanel* panel, QUndoCommand* parent = nullptr) + : OrderListCommonQtCommand(comId, panel, redrawAll, parent) {} +}; + +template +using OrderListCommonQtCommandRedrawAll = OrderListCommonQtCommandRedraw; + +template +using OrderListCommonQtCommandRedrawText = OrderListCommonQtCommandRedraw; + +class OrderListEntryQtCommand : public OrderListCommonQtCommand +{ +public: + void undo() override; + bool mergeWith(const QUndoCommand* other) override; + +protected: + OrderListEntryQtCommand(CommandId id, OrderListPanel* panel, bool redrawAll, + const OrderPosition& pos, bool secondEntry, QUndoCommand* parent); + +private: + const OrderPosition pos_; + bool isSecondEntry_; +}; + +template +class OrderListEntryQtCommandRedraw final : public OrderListEntryQtCommand +{ +public: + OrderListEntryQtCommandRedraw(OrderListPanel* panel, const OrderPosition& pos, + bool secondEntry, QUndoCommand* parent = nullptr) + : OrderListEntryQtCommand(comId, panel, redrawAll, pos, secondEntry, parent) {} +}; + +template +using OrderListEntryQtCommandRedrawAll = OrderListEntryQtCommandRedraw; + +template +using OrderListEntryQtCommandRedrawText = OrderListEntryQtCommandRedraw; +} + +#endif // ORDER_LIST_COMMON_QT_COMMAND_HPP diff -Nru bambootracker-0.4.5+git20121209/BambooTracker/gui/command/order/paste_copied_data_to_order_qt_command.cpp bambootracker-0.4.6/BambooTracker/gui/command/order/paste_copied_data_to_order_qt_command.cpp --- bambootracker-0.4.5+git20121209/BambooTracker/gui/command/order/paste_copied_data_to_order_qt_command.cpp 2020-12-08 14:05:28.000000000 +0000 +++ bambootracker-0.4.6/BambooTracker/gui/command/order/paste_copied_data_to_order_qt_command.cpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,50 +0,0 @@ -/* - * Copyright (C) 2018-2020 Rerrah - * - * Permission is hereby granted, free of charge, to any person - * obtaining a copy of this software and associated documentation - * files (the "Software"), to deal in the Software without - * restriction, including without limitation the rights to use, - * copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following - * conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES - * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT - * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, - * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR - * OTHER DEALINGS IN THE SOFTWARE. - */ - -#include "paste_copied_data_to_order_qt_command.hpp" -#include "command/command_id.hpp" - -PasteCopiedDataToOrderQtCommand::PasteCopiedDataToOrderQtCommand(OrderListPanel* panel, QUndoCommand* parent) - : QUndoCommand(parent), - panel_(panel) -{ -} - -void PasteCopiedDataToOrderQtCommand::redo() -{ - panel_->onOrderEdited(); - panel_->redrawByPatternChanged(); -} - -void PasteCopiedDataToOrderQtCommand::undo() -{ - panel_->redrawByPatternChanged(); - panel_->onOrderEdited(); -} - -int PasteCopiedDataToOrderQtCommand::id() const -{ - return CommandId::PasteCopiedDataToOrder; -} diff -Nru bambootracker-0.4.5+git20121209/BambooTracker/gui/command/order/paste_copied_data_to_order_qt_command.hpp bambootracker-0.4.6/BambooTracker/gui/command/order/paste_copied_data_to_order_qt_command.hpp --- bambootracker-0.4.5+git20121209/BambooTracker/gui/command/order/paste_copied_data_to_order_qt_command.hpp 2020-12-08 14:05:28.000000000 +0000 +++ bambootracker-0.4.6/BambooTracker/gui/command/order/paste_copied_data_to_order_qt_command.hpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,44 +0,0 @@ -/* - * Copyright (C) 2018-2018 Rerrah - * - * Permission is hereby granted, free of charge, to any person - * obtaining a copy of this software and associated documentation - * files (the "Software"), to deal in the Software without - * restriction, including without limitation the rights to use, - * copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following - * conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES - * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT - * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, - * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR - * OTHER DEALINGS IN THE SOFTWARE. - */ - -#ifndef PASTE_COPIED_DATA_TO_ORDER_QT_COMMAND_HPP -#define PASTE_COPIED_DATA_TO_ORDER_QT_COMMAND_HPP - -#include -#include "gui/order_list_editor/order_list_panel.hpp" - -class PasteCopiedDataToOrderQtCommand : public QUndoCommand -{ -public: - PasteCopiedDataToOrderQtCommand(OrderListPanel* panel, QUndoCommand* parent = nullptr); - void redo() Q_DECL_OVERRIDE; - void undo() Q_DECL_OVERRIDE; - int id() const Q_DECL_OVERRIDE; - -private: - OrderListPanel* panel_; -}; - -#endif // PASTE_COPIED_DATA_TO_ORDER_QT_COMMAND_HPP diff -Nru bambootracker-0.4.5+git20121209/BambooTracker/gui/command/order/set_pattern_to_order_qt_command.cpp bambootracker-0.4.6/BambooTracker/gui/command/order/set_pattern_to_order_qt_command.cpp --- bambootracker-0.4.5+git20121209/BambooTracker/gui/command/order/set_pattern_to_order_qt_command.cpp 2020-12-08 14:05:28.000000000 +0000 +++ bambootracker-0.4.6/BambooTracker/gui/command/order/set_pattern_to_order_qt_command.cpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,68 +0,0 @@ -/* - * Copyright (C) 2018-2020 Rerrah - * - * Permission is hereby granted, free of charge, to any person - * obtaining a copy of this software and associated documentation - * files (the "Software"), to deal in the Software without - * restriction, including without limitation the rights to use, - * copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following - * conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES - * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT - * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, - * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR - * OTHER DEALINGS IN THE SOFTWARE. - */ - -#include "set_pattern_to_order_qt_command.hpp" -#include "command/command_id.hpp" - -SetPatternToOrderQtCommand::SetPatternToOrderQtCommand(OrderListPanel* panel, OrderPosition pos, bool secondEntry, QUndoCommand* parent) - : QUndoCommand(parent), - panel_(panel), - pos_(pos), - isSecondEntry_(secondEntry) -{ -} - -void SetPatternToOrderQtCommand::redo() -{ - panel_->onOrderEdited(); - panel_->redrawByPatternChanged(); -} - -void SetPatternToOrderQtCommand::undo() -{ - panel_->onOrderEdited(); - panel_->redrawByPatternChanged(); - panel_->resetEntryCount(); -} - -int SetPatternToOrderQtCommand::id() const -{ - return CommandId::SetPatternToOrder; -} - -bool SetPatternToOrderQtCommand::mergeWith(const QUndoCommand* other) -{ - if (other->id() == id() && !isSecondEntry_) { - auto com = dynamic_cast(other); - if (com->pos_ == pos_ && com->isSecondEntry_) { - isSecondEntry_ = true; - redo(); - return true; - } - } - - isSecondEntry_ = true; - return false; -} diff -Nru bambootracker-0.4.5+git20121209/BambooTracker/gui/command/order/set_pattern_to_order_qt_command.hpp bambootracker-0.4.6/BambooTracker/gui/command/order/set_pattern_to_order_qt_command.hpp --- bambootracker-0.4.5+git20121209/BambooTracker/gui/command/order/set_pattern_to_order_qt_command.hpp 2020-12-08 14:05:28.000000000 +0000 +++ bambootracker-0.4.6/BambooTracker/gui/command/order/set_pattern_to_order_qt_command.hpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,48 +0,0 @@ -/* - * Copyright (C) 2018-2020 Rerrah - * - * Permission is hereby granted, free of charge, to any person - * obtaining a copy of this software and associated documentation - * files (the "Software"), to deal in the Software without - * restriction, including without limitation the rights to use, - * copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following - * conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES - * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT - * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, - * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR - * OTHER DEALINGS IN THE SOFTWARE. - */ - -#ifndef SET_PATTERN_TO_ORDER_QT_COMMAND_HPP -#define SET_PATTERN_TO_ORDER_QT_COMMAND_HPP - -#include -#include "gui/order_list_editor/order_list_panel.hpp" -#include "gui/order_list_editor/order_position.hpp" - -class SetPatternToOrderQtCommand : public QUndoCommand -{ -public: - SetPatternToOrderQtCommand(OrderListPanel* panel, OrderPosition pos, bool secondEntry, QUndoCommand* parent = nullptr); - void redo() Q_DECL_OVERRIDE; - void undo() Q_DECL_OVERRIDE; - int id() const Q_DECL_OVERRIDE; - bool mergeWith(const QUndoCommand* other) Q_DECL_OVERRIDE; - -private: - OrderListPanel* panel_; - const OrderPosition pos_; - bool isSecondEntry_; -}; - -#endif // SET_PATTERN_TO_ORDER_QT_COMMAND_HPP diff -Nru bambootracker-0.4.5+git20121209/BambooTracker/gui/command/pattern/change_values_in_pattern_qt_command.cpp bambootracker-0.4.6/BambooTracker/gui/command/pattern/change_values_in_pattern_qt_command.cpp --- bambootracker-0.4.5+git20121209/BambooTracker/gui/command/pattern/change_values_in_pattern_qt_command.cpp 2020-12-08 14:05:28.000000000 +0000 +++ bambootracker-0.4.6/BambooTracker/gui/command/pattern/change_values_in_pattern_qt_command.cpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,48 +0,0 @@ -/* - * Copyright (C) 2018-2020 Rerrah - * - * Permission is hereby granted, free of charge, to any person - * obtaining a copy of this software and associated documentation - * files (the "Software"), to deal in the Software without - * restriction, including without limitation the rights to use, - * copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following - * conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES - * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT - * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, - * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR - * OTHER DEALINGS IN THE SOFTWARE. - */ - -#include "change_values_in_pattern_qt_command.hpp" -#include "command/command_id.hpp" - -ChangeValuesInPatternQtCommand::ChangeValuesInPatternQtCommand(PatternEditorPanel* panel, QUndoCommand* parent) - : QUndoCommand(parent), - panel_(panel) -{ -} - -void ChangeValuesInPatternQtCommand::redo() -{ - panel_->redrawByPatternChanged(true); -} - -void ChangeValuesInPatternQtCommand::undo() -{ - panel_->redrawByPatternChanged(true); -} - -int ChangeValuesInPatternQtCommand::id() const -{ - return CommandId::ChangeValuesInPattern; -} diff -Nru bambootracker-0.4.5+git20121209/BambooTracker/gui/command/pattern/change_values_in_pattern_qt_command.hpp bambootracker-0.4.6/BambooTracker/gui/command/pattern/change_values_in_pattern_qt_command.hpp --- bambootracker-0.4.5+git20121209/BambooTracker/gui/command/pattern/change_values_in_pattern_qt_command.hpp 2020-12-08 14:05:28.000000000 +0000 +++ bambootracker-0.4.6/BambooTracker/gui/command/pattern/change_values_in_pattern_qt_command.hpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,44 +0,0 @@ -/* - * Copyright (C) 2018-2020 Rerrah - * - * Permission is hereby granted, free of charge, to any person - * obtaining a copy of this software and associated documentation - * files (the "Software"), to deal in the Software without - * restriction, including without limitation the rights to use, - * copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following - * conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES - * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT - * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, - * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR - * OTHER DEALINGS IN THE SOFTWARE. - */ - -#ifndef ADDVALUEINPATTERNQTCOMMAND_HPP -#define ADDVALUEINPATTERNQTCOMMAND_HPP - -#include -#include "gui/pattern_editor/pattern_editor_panel.hpp" - -class ChangeValuesInPatternQtCommand : public QUndoCommand -{ -public: - ChangeValuesInPatternQtCommand(PatternEditorPanel* panel, QUndoCommand* parent = nullptr); - void redo() Q_DECL_OVERRIDE; - void undo() Q_DECL_OVERRIDE; - int id() const Q_DECL_OVERRIDE; - -private: - PatternEditorPanel* panel_; -}; - -#endif // ADDVALUEINPATTERNQTCOMMAND_HPP diff -Nru bambootracker-0.4.5+git20121209/BambooTracker/gui/command/pattern/delete_previous_step_qt_command.cpp bambootracker-0.4.6/BambooTracker/gui/command/pattern/delete_previous_step_qt_command.cpp --- bambootracker-0.4.5+git20121209/BambooTracker/gui/command/pattern/delete_previous_step_qt_command.cpp 2020-12-08 14:05:28.000000000 +0000 +++ bambootracker-0.4.6/BambooTracker/gui/command/pattern/delete_previous_step_qt_command.cpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,48 +0,0 @@ -/* - * Copyright (C) 2018-2019 Rerrah - * - * Permission is hereby granted, free of charge, to any person - * obtaining a copy of this software and associated documentation - * files (the "Software"), to deal in the Software without - * restriction, including without limitation the rights to use, - * copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following - * conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES - * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT - * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, - * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR - * OTHER DEALINGS IN THE SOFTWARE. - */ - -#include "delete_previous_step_qt_command.hpp" -#include "command/command_id.hpp" - -DeletePreviousStepQtCommand::DeletePreviousStepQtCommand(PatternEditorPanel* panel, QUndoCommand* parent) - : QUndoCommand(parent), - panel_(panel) -{ -} - -void DeletePreviousStepQtCommand::redo() -{ - panel_->redrawByPatternChanged(true); -} - -void DeletePreviousStepQtCommand::undo() -{ - panel_->redrawByPatternChanged(true); -} - -int DeletePreviousStepQtCommand::id() const -{ - return CommandId::DeletePreviousStep; -} diff -Nru bambootracker-0.4.5+git20121209/BambooTracker/gui/command/pattern/delete_previous_step_qt_command.hpp bambootracker-0.4.6/BambooTracker/gui/command/pattern/delete_previous_step_qt_command.hpp --- bambootracker-0.4.5+git20121209/BambooTracker/gui/command/pattern/delete_previous_step_qt_command.hpp 2020-12-08 14:05:28.000000000 +0000 +++ bambootracker-0.4.6/BambooTracker/gui/command/pattern/delete_previous_step_qt_command.hpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,44 +0,0 @@ -/* - * Copyright (C) 2018 Rerrah - * - * Permission is hereby granted, free of charge, to any person - * obtaining a copy of this software and associated documentation - * files (the "Software"), to deal in the Software without - * restriction, including without limitation the rights to use, - * copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following - * conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES - * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT - * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, - * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR - * OTHER DEALINGS IN THE SOFTWARE. - */ - -#ifndef DELETE_PREVIOUS_STEP_QT_COMMAND_HPP -#define DELETE_PREVIOUS_STEP_QT_COMMAND_HPP - -#include -#include "gui/pattern_editor/pattern_editor_panel.hpp" - -class DeletePreviousStepQtCommand : public QUndoCommand -{ -public: - DeletePreviousStepQtCommand(PatternEditorPanel* panel, QUndoCommand* parent = nullptr); - void redo() Q_DECL_OVERRIDE; - void undo() Q_DECL_OVERRIDE; - int id() const Q_DECL_OVERRIDE; - -private: - PatternEditorPanel* panel_; -}; - -#endif // DELETE_PREVIOUS_STEP_QT_COMMAND_HPP diff -Nru bambootracker-0.4.5+git20121209/BambooTracker/gui/command/pattern/erase_cells_in_pattern_qt_command.cpp bambootracker-0.4.6/BambooTracker/gui/command/pattern/erase_cells_in_pattern_qt_command.cpp --- bambootracker-0.4.5+git20121209/BambooTracker/gui/command/pattern/erase_cells_in_pattern_qt_command.cpp 2020-12-08 14:05:28.000000000 +0000 +++ bambootracker-0.4.6/BambooTracker/gui/command/pattern/erase_cells_in_pattern_qt_command.cpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,48 +0,0 @@ -/* - * Copyright (C) 2018-2019 Rerrah - * - * Permission is hereby granted, free of charge, to any person - * obtaining a copy of this software and associated documentation - * files (the "Software"), to deal in the Software without - * restriction, including without limitation the rights to use, - * copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following - * conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES - * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT - * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, - * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR - * OTHER DEALINGS IN THE SOFTWARE. - */ - -#include "erase_cells_in_pattern_qt_command.hpp" -#include "command/command_id.hpp" - -EraseCellsInPatternQtCommand::EraseCellsInPatternQtCommand(PatternEditorPanel* panel, QUndoCommand* parent) - : QUndoCommand(parent), - panel_(panel) -{ -} - -void EraseCellsInPatternQtCommand::redo() -{ - panel_->redrawByPatternChanged(true); -} - -void EraseCellsInPatternQtCommand::undo() -{ - panel_->redrawByPatternChanged(true); -} - -int EraseCellsInPatternQtCommand::id() const -{ - return CommandId::EraseCellsInPattern; -} diff -Nru bambootracker-0.4.5+git20121209/BambooTracker/gui/command/pattern/erase_cells_in_pattern_qt_command.hpp bambootracker-0.4.6/BambooTracker/gui/command/pattern/erase_cells_in_pattern_qt_command.hpp --- bambootracker-0.4.5+git20121209/BambooTracker/gui/command/pattern/erase_cells_in_pattern_qt_command.hpp 2020-12-08 14:05:28.000000000 +0000 +++ bambootracker-0.4.6/BambooTracker/gui/command/pattern/erase_cells_in_pattern_qt_command.hpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,44 +0,0 @@ -/* - * Copyright (C) 2018 Rerrah - * - * Permission is hereby granted, free of charge, to any person - * obtaining a copy of this software and associated documentation - * files (the "Software"), to deal in the Software without - * restriction, including without limitation the rights to use, - * copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following - * conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES - * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT - * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, - * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR - * OTHER DEALINGS IN THE SOFTWARE. - */ - -#ifndef ERASE_CELLS_IN_PATTERN_QT_COMMAND_HPP -#define ERASE_CELLS_IN_PATTERN_QT_COMMAND_HPP - -#include -#include "gui/pattern_editor/pattern_editor_panel.hpp" - -class EraseCellsInPatternQtCommand : public QUndoCommand -{ -public: - EraseCellsInPatternQtCommand(PatternEditorPanel* panel, QUndoCommand* parent = nullptr); - void redo() Q_DECL_OVERRIDE; - void undo() Q_DECL_OVERRIDE; - int id() const Q_DECL_OVERRIDE; - -private: - PatternEditorPanel* panel_; -}; - -#endif // ERASE_CELLS_IN_PATTERN_QT_COMMAND_HPP diff -Nru bambootracker-0.4.5+git20121209/BambooTracker/gui/command/pattern/erase_effect_in_step_qt_command.cpp bambootracker-0.4.6/BambooTracker/gui/command/pattern/erase_effect_in_step_qt_command.cpp --- bambootracker-0.4.5+git20121209/BambooTracker/gui/command/pattern/erase_effect_in_step_qt_command.cpp 2020-12-08 14:05:28.000000000 +0000 +++ bambootracker-0.4.6/BambooTracker/gui/command/pattern/erase_effect_in_step_qt_command.cpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,48 +0,0 @@ -/* - * Copyright (C) 2018-2019 Rerrah - * - * Permission is hereby granted, free of charge, to any person - * obtaining a copy of this software and associated documentation - * files (the "Software"), to deal in the Software without - * restriction, including without limitation the rights to use, - * copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following - * conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES - * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT - * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, - * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR - * OTHER DEALINGS IN THE SOFTWARE. - */ - -#include "erase_effect_in_step_qt_command.hpp" -#include "command/command_id.hpp" - -EraseEffectInStepQtCommand::EraseEffectInStepQtCommand(PatternEditorPanel* panel, QUndoCommand* parent) - : QUndoCommand(parent), - panel_(panel) -{ -} - -void EraseEffectInStepQtCommand::redo() -{ - panel_->redrawByPatternChanged(true); -} - -void EraseEffectInStepQtCommand::undo() -{ - panel_->redrawByPatternChanged(true); -} - -int EraseEffectInStepQtCommand::id() const -{ - return CommandId::EraseEffectInStep; -} diff -Nru bambootracker-0.4.5+git20121209/BambooTracker/gui/command/pattern/erase_effect_in_step_qt_command.hpp bambootracker-0.4.6/BambooTracker/gui/command/pattern/erase_effect_in_step_qt_command.hpp --- bambootracker-0.4.5+git20121209/BambooTracker/gui/command/pattern/erase_effect_in_step_qt_command.hpp 2020-12-08 14:05:28.000000000 +0000 +++ bambootracker-0.4.6/BambooTracker/gui/command/pattern/erase_effect_in_step_qt_command.hpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,44 +0,0 @@ -/* - * Copyright (C) 2018 Rerrah - * - * Permission is hereby granted, free of charge, to any person - * obtaining a copy of this software and associated documentation - * files (the "Software"), to deal in the Software without - * restriction, including without limitation the rights to use, - * copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following - * conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES - * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT - * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, - * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR - * OTHER DEALINGS IN THE SOFTWARE. - */ - -#ifndef ERASE_EFFECT_IN_STEP_QT_COMMAND_HPP -#define ERASE_EFFECT_IN_STEP_QT_COMMAND_HPP - -#include -#include "gui/pattern_editor/pattern_editor_panel.hpp" - -class EraseEffectInStepQtCommand : public QUndoCommand -{ -public: - EraseEffectInStepQtCommand(PatternEditorPanel* panel, QUndoCommand* parent = nullptr); - void redo() Q_DECL_OVERRIDE; - void undo() Q_DECL_OVERRIDE; - int id() const Q_DECL_OVERRIDE; - -private: - PatternEditorPanel* panel_; -}; - -#endif // ERASE_EFFECT_IN_STEP_QT_COMMAND_HPP diff -Nru bambootracker-0.4.5+git20121209/BambooTracker/gui/command/pattern/erase_effect_value_in_step_qt_command.cpp bambootracker-0.4.6/BambooTracker/gui/command/pattern/erase_effect_value_in_step_qt_command.cpp --- bambootracker-0.4.5+git20121209/BambooTracker/gui/command/pattern/erase_effect_value_in_step_qt_command.cpp 2020-12-08 14:05:28.000000000 +0000 +++ bambootracker-0.4.6/BambooTracker/gui/command/pattern/erase_effect_value_in_step_qt_command.cpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,48 +0,0 @@ -/* - * Copyright (C) 2018-2019 Rerrah - * - * Permission is hereby granted, free of charge, to any person - * obtaining a copy of this software and associated documentation - * files (the "Software"), to deal in the Software without - * restriction, including without limitation the rights to use, - * copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following - * conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES - * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT - * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, - * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR - * OTHER DEALINGS IN THE SOFTWARE. - */ - -#include "erase_effect_value_in_step_qt_command.hpp" -#include "command/command_id.hpp" - -EraseEffectValueInStepQtCommand::EraseEffectValueInStepQtCommand(PatternEditorPanel* panel, QUndoCommand* parent) - : QUndoCommand(parent), - panel_(panel) -{ -} - -void EraseEffectValueInStepQtCommand::redo() -{ - panel_->redrawByPatternChanged(true); -} - -void EraseEffectValueInStepQtCommand::undo() -{ - panel_->redrawByPatternChanged(true); -} - -int EraseEffectValueInStepQtCommand::id() const -{ - return CommandId::EraseEffectValueInStep; -} diff -Nru bambootracker-0.4.5+git20121209/BambooTracker/gui/command/pattern/erase_effect_value_in_step_qt_command.hpp bambootracker-0.4.6/BambooTracker/gui/command/pattern/erase_effect_value_in_step_qt_command.hpp --- bambootracker-0.4.5+git20121209/BambooTracker/gui/command/pattern/erase_effect_value_in_step_qt_command.hpp 2020-12-08 14:05:28.000000000 +0000 +++ bambootracker-0.4.6/BambooTracker/gui/command/pattern/erase_effect_value_in_step_qt_command.hpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,44 +0,0 @@ -/* - * Copyright (C) 2018 Rerrah - * - * Permission is hereby granted, free of charge, to any person - * obtaining a copy of this software and associated documentation - * files (the "Software"), to deal in the Software without - * restriction, including without limitation the rights to use, - * copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following - * conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES - * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT - * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, - * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR - * OTHER DEALINGS IN THE SOFTWARE. - */ - -#ifndef ERASE_EFFECT_VALUE_IN_STEP_QT_COMMAND_HPP -#define ERASE_EFFECT_VALUE_IN_STEP_QT_COMMAND_HPP - -#include -#include "gui/pattern_editor/pattern_editor_panel.hpp" - -class EraseEffectValueInStepQtCommand : public QUndoCommand -{ -public: - EraseEffectValueInStepQtCommand(PatternEditorPanel* panel, QUndoCommand* parent = nullptr); - void redo() Q_DECL_OVERRIDE; - void undo() Q_DECL_OVERRIDE; - int id() const Q_DECL_OVERRIDE; - -private: - PatternEditorPanel* panel_; -}; - -#endif // ERASE_EFFECT_VALUE_IN_STEP_QT_COMMAND_HPP diff -Nru bambootracker-0.4.5+git20121209/BambooTracker/gui/command/pattern/erase_instrument_in_step_qt_command.cpp bambootracker-0.4.6/BambooTracker/gui/command/pattern/erase_instrument_in_step_qt_command.cpp --- bambootracker-0.4.5+git20121209/BambooTracker/gui/command/pattern/erase_instrument_in_step_qt_command.cpp 2020-12-08 14:05:28.000000000 +0000 +++ bambootracker-0.4.6/BambooTracker/gui/command/pattern/erase_instrument_in_step_qt_command.cpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,48 +0,0 @@ -/* - * Copyright (C) 2018-2019 Rerrah - * - * Permission is hereby granted, free of charge, to any person - * obtaining a copy of this software and associated documentation - * files (the "Software"), to deal in the Software without - * restriction, including without limitation the rights to use, - * copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following - * conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES - * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT - * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, - * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR - * OTHER DEALINGS IN THE SOFTWARE. - */ - -#include "erase_instrument_in_step_qt_command.hpp" -#include "command/command_id.hpp" - -EraseInstrumentInStepQtCommand::EraseInstrumentInStepQtCommand(PatternEditorPanel* panel, QUndoCommand* parent) - : QUndoCommand(parent), - panel_(panel) -{ -} - -void EraseInstrumentInStepQtCommand::redo() -{ - panel_->redrawByPatternChanged(); -} - -void EraseInstrumentInStepQtCommand::undo() -{ - panel_->redrawByPatternChanged(); -} - -int EraseInstrumentInStepQtCommand::id() const -{ - return CommandId::EraseInstrumentInStep; -} diff -Nru bambootracker-0.4.5+git20121209/BambooTracker/gui/command/pattern/erase_instrument_in_step_qt_command.hpp bambootracker-0.4.6/BambooTracker/gui/command/pattern/erase_instrument_in_step_qt_command.hpp --- bambootracker-0.4.5+git20121209/BambooTracker/gui/command/pattern/erase_instrument_in_step_qt_command.hpp 2020-12-08 14:05:28.000000000 +0000 +++ bambootracker-0.4.6/BambooTracker/gui/command/pattern/erase_instrument_in_step_qt_command.hpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,44 +0,0 @@ -/* - * Copyright (C) 2018 Rerrah - * - * Permission is hereby granted, free of charge, to any person - * obtaining a copy of this software and associated documentation - * files (the "Software"), to deal in the Software without - * restriction, including without limitation the rights to use, - * copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following - * conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES - * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT - * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, - * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR - * OTHER DEALINGS IN THE SOFTWARE. - */ - -#ifndef ERASE_INSTRUMENT_IN_STEP_QT_COMMAND_HPP -#define ERASE_INSTRUMENT_IN_STEP_QT_COMMAND_HPP - -#include -#include "gui/pattern_editor/pattern_editor_panel.hpp" - -class EraseInstrumentInStepQtCommand : public QUndoCommand -{ -public: - EraseInstrumentInStepQtCommand(PatternEditorPanel* panel, QUndoCommand* parent = nullptr); - void redo() Q_DECL_OVERRIDE; - void undo() Q_DECL_OVERRIDE; - int id() const Q_DECL_OVERRIDE; - -private: - PatternEditorPanel* panel_; -}; - -#endif // ERASE_INSTRUMENT_IN_STEP_QT_COMMAND_HPP diff -Nru bambootracker-0.4.5+git20121209/BambooTracker/gui/command/pattern/erase_step_qt_command.cpp bambootracker-0.4.6/BambooTracker/gui/command/pattern/erase_step_qt_command.cpp --- bambootracker-0.4.5+git20121209/BambooTracker/gui/command/pattern/erase_step_qt_command.cpp 2020-12-08 14:05:28.000000000 +0000 +++ bambootracker-0.4.6/BambooTracker/gui/command/pattern/erase_step_qt_command.cpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,48 +0,0 @@ -/* - * Copyright (C) 2018-2019 Rerrah - * - * Permission is hereby granted, free of charge, to any person - * obtaining a copy of this software and associated documentation - * files (the "Software"), to deal in the Software without - * restriction, including without limitation the rights to use, - * copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following - * conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES - * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT - * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, - * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR - * OTHER DEALINGS IN THE SOFTWARE. - */ - -#include "erase_step_qt_command.hpp" -#include "command/command_id.hpp" - -EraseStepQtCommand::EraseStepQtCommand(PatternEditorPanel* panel, QUndoCommand* parent) - : QUndoCommand(parent), - panel_(panel) -{ -} - -void EraseStepQtCommand::redo() -{ - panel_->redrawByPatternChanged(true); -} - -void EraseStepQtCommand::undo() -{ - panel_->redrawByPatternChanged(true); -} - -int EraseStepQtCommand::id() const -{ - return CommandId::EraseStep; -} diff -Nru bambootracker-0.4.5+git20121209/BambooTracker/gui/command/pattern/erase_step_qt_command.hpp bambootracker-0.4.6/BambooTracker/gui/command/pattern/erase_step_qt_command.hpp --- bambootracker-0.4.5+git20121209/BambooTracker/gui/command/pattern/erase_step_qt_command.hpp 2020-12-08 14:05:28.000000000 +0000 +++ bambootracker-0.4.6/BambooTracker/gui/command/pattern/erase_step_qt_command.hpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,44 +0,0 @@ -/* - * Copyright (C) 2018 Rerrah - * - * Permission is hereby granted, free of charge, to any person - * obtaining a copy of this software and associated documentation - * files (the "Software"), to deal in the Software without - * restriction, including without limitation the rights to use, - * copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following - * conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES - * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT - * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, - * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR - * OTHER DEALINGS IN THE SOFTWARE. - */ - -#ifndef ERASE_STEP_QT_COMMAND_HPP -#define ERASE_STEP_QT_COMMAND_HPP - -#include -#include "gui/pattern_editor/pattern_editor_panel.hpp" - -class EraseStepQtCommand : public QUndoCommand -{ -public: - EraseStepQtCommand(PatternEditorPanel* panel, QUndoCommand* parent = nullptr); - void redo() Q_DECL_OVERRIDE; - void undo() Q_DECL_OVERRIDE; - int id() const Q_DECL_OVERRIDE; - -private: - PatternEditorPanel* panel_; -}; - -#endif // ERASE_STEP_QT_COMMAND_HPP diff -Nru bambootracker-0.4.5+git20121209/BambooTracker/gui/command/pattern/erase_volume_in_step_qt_command.cpp bambootracker-0.4.6/BambooTracker/gui/command/pattern/erase_volume_in_step_qt_command.cpp --- bambootracker-0.4.5+git20121209/BambooTracker/gui/command/pattern/erase_volume_in_step_qt_command.cpp 2020-12-08 14:05:28.000000000 +0000 +++ bambootracker-0.4.6/BambooTracker/gui/command/pattern/erase_volume_in_step_qt_command.cpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,48 +0,0 @@ -/* - * Copyright (C) 2018-2019 Rerrah - * - * Permission is hereby granted, free of charge, to any person - * obtaining a copy of this software and associated documentation - * files (the "Software"), to deal in the Software without - * restriction, including without limitation the rights to use, - * copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following - * conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES - * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT - * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, - * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR - * OTHER DEALINGS IN THE SOFTWARE. - */ - -#include "erase_volume_in_step_qt_command.hpp" -#include "command/command_id.hpp" - -EraseVolumeInStepQtCommand::EraseVolumeInStepQtCommand(PatternEditorPanel* panel, QUndoCommand* parent) - : QUndoCommand(parent), - panel_(panel) -{ -} - -void EraseVolumeInStepQtCommand::redo() -{ - panel_->redrawByPatternChanged(); -} - -void EraseVolumeInStepQtCommand::undo() -{ - panel_->redrawByPatternChanged(); -} - -int EraseVolumeInStepQtCommand::id() const -{ - return CommandId::EraseVolumeInStep; -} diff -Nru bambootracker-0.4.5+git20121209/BambooTracker/gui/command/pattern/erase_volume_in_step_qt_command.hpp bambootracker-0.4.6/BambooTracker/gui/command/pattern/erase_volume_in_step_qt_command.hpp --- bambootracker-0.4.5+git20121209/BambooTracker/gui/command/pattern/erase_volume_in_step_qt_command.hpp 2020-12-08 14:05:28.000000000 +0000 +++ bambootracker-0.4.6/BambooTracker/gui/command/pattern/erase_volume_in_step_qt_command.hpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,44 +0,0 @@ -/* - * Copyright (C) 2018 Rerrah - * - * Permission is hereby granted, free of charge, to any person - * obtaining a copy of this software and associated documentation - * files (the "Software"), to deal in the Software without - * restriction, including without limitation the rights to use, - * copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following - * conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES - * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT - * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, - * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR - * OTHER DEALINGS IN THE SOFTWARE. - */ - -#ifndef ERASE_VOLUME_IN_STEP_QT_COMMAND_HPP -#define ERASE_VOLUME_IN_STEP_QT_COMMAND_HPP - -#include -#include "gui/pattern_editor/pattern_editor_panel.hpp" - -class EraseVolumeInStepQtCommand : public QUndoCommand -{ -public: - EraseVolumeInStepQtCommand(PatternEditorPanel* panel, QUndoCommand* parent = nullptr); - void redo() Q_DECL_OVERRIDE; - void undo() Q_DECL_OVERRIDE; - int id() const Q_DECL_OVERRIDE; - -private: - PatternEditorPanel* panel_; -}; - -#endif // ERASE_VOLUME_IN_STEP_QT_COMMAND_HPP diff -Nru bambootracker-0.4.5+git20121209/BambooTracker/gui/command/pattern/expand_pattern_qt_command.cpp bambootracker-0.4.6/BambooTracker/gui/command/pattern/expand_pattern_qt_command.cpp --- bambootracker-0.4.5+git20121209/BambooTracker/gui/command/pattern/expand_pattern_qt_command.cpp 2020-12-08 14:05:28.000000000 +0000 +++ bambootracker-0.4.6/BambooTracker/gui/command/pattern/expand_pattern_qt_command.cpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,48 +0,0 @@ -/* - * Copyright (C) 2018-2019 Rerrah - * - * Permission is hereby granted, free of charge, to any person - * obtaining a copy of this software and associated documentation - * files (the "Software"), to deal in the Software without - * restriction, including without limitation the rights to use, - * copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following - * conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES - * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT - * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, - * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR - * OTHER DEALINGS IN THE SOFTWARE. - */ - -#include "expand_pattern_qt_command.hpp" -#include "command/command_id.hpp" - -ExpandPatternQtCommand::ExpandPatternQtCommand(PatternEditorPanel* panel, QUndoCommand* parent) - : QUndoCommand(parent), - panel_(panel) -{ -} - -void ExpandPatternQtCommand::redo() -{ - panel_->redrawByPatternChanged(true); -} - -void ExpandPatternQtCommand::undo() -{ - panel_->redrawByPatternChanged(true); -} - -int ExpandPatternQtCommand::id() const -{ - return CommandId::ExpandPattern; -} diff -Nru bambootracker-0.4.5+git20121209/BambooTracker/gui/command/pattern/expand_pattern_qt_command.hpp bambootracker-0.4.6/BambooTracker/gui/command/pattern/expand_pattern_qt_command.hpp --- bambootracker-0.4.5+git20121209/BambooTracker/gui/command/pattern/expand_pattern_qt_command.hpp 2020-12-08 14:05:28.000000000 +0000 +++ bambootracker-0.4.6/BambooTracker/gui/command/pattern/expand_pattern_qt_command.hpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,44 +0,0 @@ -/* - * Copyright (C) 2018 Rerrah - * - * Permission is hereby granted, free of charge, to any person - * obtaining a copy of this software and associated documentation - * files (the "Software"), to deal in the Software without - * restriction, including without limitation the rights to use, - * copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following - * conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES - * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT - * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, - * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR - * OTHER DEALINGS IN THE SOFTWARE. - */ - -#ifndef EXPAND_PATTERN_QT_COMMAND_HPP -#define EXPAND_PATTERN_QT_COMMAND_HPP - -#include -#include "gui/pattern_editor/pattern_editor_panel.hpp" - -class ExpandPatternQtCommand : public QUndoCommand -{ -public: - ExpandPatternQtCommand(PatternEditorPanel* panel, QUndoCommand* parent = nullptr); - void redo() Q_DECL_OVERRIDE; - void undo() Q_DECL_OVERRIDE; - int id() const Q_DECL_OVERRIDE; - -private: - PatternEditorPanel* panel_; -}; - -#endif // EXPAND_PATTERN_QT_COMMAND_HPP diff -Nru bambootracker-0.4.5+git20121209/BambooTracker/gui/command/pattern/insert_step_qt_command.cpp bambootracker-0.4.6/BambooTracker/gui/command/pattern/insert_step_qt_command.cpp --- bambootracker-0.4.5+git20121209/BambooTracker/gui/command/pattern/insert_step_qt_command.cpp 2020-12-08 14:05:28.000000000 +0000 +++ bambootracker-0.4.6/BambooTracker/gui/command/pattern/insert_step_qt_command.cpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,48 +0,0 @@ -/* - * Copyright (C) 2018-2019 Rerrah - * - * Permission is hereby granted, free of charge, to any person - * obtaining a copy of this software and associated documentation - * files (the "Software"), to deal in the Software without - * restriction, including without limitation the rights to use, - * copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following - * conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES - * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT - * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, - * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR - * OTHER DEALINGS IN THE SOFTWARE. - */ - -#include "insert_step_qt_command.hpp" -#include "command/command_id.hpp" - -InsertStepQtCommand::InsertStepQtCommand(PatternEditorPanel* panel, QUndoCommand* parent) - : QUndoCommand(parent), - panel_(panel) -{ -} - -void InsertStepQtCommand::redo() -{ - panel_->redrawByPatternChanged(true); -} - -void InsertStepQtCommand::undo() -{ - panel_->redrawByPatternChanged(true); -} - -int InsertStepQtCommand::id() const -{ - return CommandId::InsertStep; -} diff -Nru bambootracker-0.4.5+git20121209/BambooTracker/gui/command/pattern/insert_step_qt_command.hpp bambootracker-0.4.6/BambooTracker/gui/command/pattern/insert_step_qt_command.hpp --- bambootracker-0.4.5+git20121209/BambooTracker/gui/command/pattern/insert_step_qt_command.hpp 2020-12-08 14:05:28.000000000 +0000 +++ bambootracker-0.4.6/BambooTracker/gui/command/pattern/insert_step_qt_command.hpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,44 +0,0 @@ -/* - * Copyright (C) 2018 Rerrah - * - * Permission is hereby granted, free of charge, to any person - * obtaining a copy of this software and associated documentation - * files (the "Software"), to deal in the Software without - * restriction, including without limitation the rights to use, - * copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following - * conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES - * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT - * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, - * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR - * OTHER DEALINGS IN THE SOFTWARE. - */ - -#ifndef INSERT_STEP_QT_COMMAND_HPP -#define INSERT_STEP_QT_COMMAND_HPP - -#include -#include "gui/pattern_editor/pattern_editor_panel.hpp" - -class InsertStepQtCommand : public QUndoCommand -{ -public: - InsertStepQtCommand(PatternEditorPanel* panel, QUndoCommand* parent = nullptr); - void redo() Q_DECL_OVERRIDE; - void undo() Q_DECL_OVERRIDE; - int id() const Q_DECL_OVERRIDE; - -private: - PatternEditorPanel* panel_; -}; - -#endif // INSERT_STEP_QT_COMMAND_HPP diff -Nru bambootracker-0.4.5+git20121209/BambooTracker/gui/command/pattern/interpolate_pattern_qt_command.cpp bambootracker-0.4.6/BambooTracker/gui/command/pattern/interpolate_pattern_qt_command.cpp --- bambootracker-0.4.5+git20121209/BambooTracker/gui/command/pattern/interpolate_pattern_qt_command.cpp 2020-12-08 14:05:28.000000000 +0000 +++ bambootracker-0.4.6/BambooTracker/gui/command/pattern/interpolate_pattern_qt_command.cpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,48 +0,0 @@ -/* - * Copyright (C) 2018-2019 Rerrah - * - * Permission is hereby granted, free of charge, to any person - * obtaining a copy of this software and associated documentation - * files (the "Software"), to deal in the Software without - * restriction, including without limitation the rights to use, - * copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following - * conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES - * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT - * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, - * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR - * OTHER DEALINGS IN THE SOFTWARE. - */ - -#include "interpolate_pattern_qt_command.hpp" -#include "command/command_id.hpp" - -InterpolatePatternQtCommand::InterpolatePatternQtCommand(PatternEditorPanel* panel, QUndoCommand* parent) - : QUndoCommand(parent), - panel_(panel) -{ -} - -void InterpolatePatternQtCommand::redo() -{ - panel_->redrawByPatternChanged(); -} - -void InterpolatePatternQtCommand::undo() -{ - panel_->redrawByPatternChanged(); -} - -int InterpolatePatternQtCommand::id() const -{ - return CommandId::InterpolatePattern; -} diff -Nru bambootracker-0.4.5+git20121209/BambooTracker/gui/command/pattern/interpolate_pattern_qt_command.hpp bambootracker-0.4.6/BambooTracker/gui/command/pattern/interpolate_pattern_qt_command.hpp --- bambootracker-0.4.5+git20121209/BambooTracker/gui/command/pattern/interpolate_pattern_qt_command.hpp 2020-12-08 14:05:28.000000000 +0000 +++ bambootracker-0.4.6/BambooTracker/gui/command/pattern/interpolate_pattern_qt_command.hpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,44 +0,0 @@ -/* - * Copyright (C) 2018 Rerrah - * - * Permission is hereby granted, free of charge, to any person - * obtaining a copy of this software and associated documentation - * files (the "Software"), to deal in the Software without - * restriction, including without limitation the rights to use, - * copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following - * conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES - * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT - * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, - * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR - * OTHER DEALINGS IN THE SOFTWARE. - */ - -#ifndef INTERPOLATE_PATTERN_QT_COMMAND_HPP -#define INTERPOLATE_PATTERN_QT_COMMAND_HPP - -#include -#include "gui/pattern_editor/pattern_editor_panel.hpp" - -class InterpolatePatternQtCommand : public QUndoCommand -{ -public: - InterpolatePatternQtCommand(PatternEditorPanel* panel, QUndoCommand* parent = nullptr); - void redo() Q_DECL_OVERRIDE; - void undo() Q_DECL_OVERRIDE; - int id() const Q_DECL_OVERRIDE; - -private: - PatternEditorPanel* panel_; -}; - -#endif // INTERPOLATE_PATTERN_QT_COMMAND_HPP diff -Nru bambootracker-0.4.5+git20121209/BambooTracker/gui/command/pattern/paste_copied_data_to_pattern_qt_command.cpp bambootracker-0.4.6/BambooTracker/gui/command/pattern/paste_copied_data_to_pattern_qt_command.cpp --- bambootracker-0.4.5+git20121209/BambooTracker/gui/command/pattern/paste_copied_data_to_pattern_qt_command.cpp 2020-12-08 14:05:28.000000000 +0000 +++ bambootracker-0.4.6/BambooTracker/gui/command/pattern/paste_copied_data_to_pattern_qt_command.cpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,48 +0,0 @@ -/* - * Copyright (C) 2018-2019 Rerrah - * - * Permission is hereby granted, free of charge, to any person - * obtaining a copy of this software and associated documentation - * files (the "Software"), to deal in the Software without - * restriction, including without limitation the rights to use, - * copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following - * conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES - * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT - * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, - * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR - * OTHER DEALINGS IN THE SOFTWARE. - */ - -#include "paste_copied_data_to_pattern_qt_command.hpp" -#include "command/command_id.hpp" - -PasteCopiedDataToPatternQtCommand::PasteCopiedDataToPatternQtCommand(PatternEditorPanel* panel, QUndoCommand* parent) - : QUndoCommand(parent), - panel_(panel) -{ -} - -void PasteCopiedDataToPatternQtCommand::redo() -{ - panel_->redrawByPatternChanged(true); -} - -void PasteCopiedDataToPatternQtCommand::undo() -{ - panel_->redrawByPatternChanged(true); -} - -int PasteCopiedDataToPatternQtCommand::id() const -{ - return CommandId::PasteCopiedDataToPattern; -} diff -Nru bambootracker-0.4.5+git20121209/BambooTracker/gui/command/pattern/paste_copied_data_to_pattern_qt_command.hpp bambootracker-0.4.6/BambooTracker/gui/command/pattern/paste_copied_data_to_pattern_qt_command.hpp --- bambootracker-0.4.5+git20121209/BambooTracker/gui/command/pattern/paste_copied_data_to_pattern_qt_command.hpp 2020-12-08 14:05:28.000000000 +0000 +++ bambootracker-0.4.6/BambooTracker/gui/command/pattern/paste_copied_data_to_pattern_qt_command.hpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,44 +0,0 @@ -/* - * Copyright (C) 2018 Rerrah - * - * Permission is hereby granted, free of charge, to any person - * obtaining a copy of this software and associated documentation - * files (the "Software"), to deal in the Software without - * restriction, including without limitation the rights to use, - * copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following - * conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES - * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT - * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, - * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR - * OTHER DEALINGS IN THE SOFTWARE. - */ - -#ifndef PASTE_COPIED_DATA_TO_PATTERN_QT_COMMAND_HPP -#define PASTE_COPIED_DATA_TO_PATTERN_QT_COMMAND_HPP - -#include -#include "gui/pattern_editor/pattern_editor_panel.hpp" - -class PasteCopiedDataToPatternQtCommand : public QUndoCommand -{ -public: - PasteCopiedDataToPatternQtCommand(PatternEditorPanel* panel, QUndoCommand* parent = nullptr); - void redo() Q_DECL_OVERRIDE; - void undo() Q_DECL_OVERRIDE; - int id() const Q_DECL_OVERRIDE; - -private: - PatternEditorPanel* panel_; -}; - -#endif // PASTE_COPIED_DATA_TO_PATTERN_QT_COMMAND_HPP diff -Nru bambootracker-0.4.5+git20121209/BambooTracker/gui/command/pattern/paste_insert_copied_data_to_pattern_qt_command.cpp bambootracker-0.4.6/BambooTracker/gui/command/pattern/paste_insert_copied_data_to_pattern_qt_command.cpp --- bambootracker-0.4.5+git20121209/BambooTracker/gui/command/pattern/paste_insert_copied_data_to_pattern_qt_command.cpp 2020-12-08 14:05:28.000000000 +0000 +++ bambootracker-0.4.6/BambooTracker/gui/command/pattern/paste_insert_copied_data_to_pattern_qt_command.cpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,48 +0,0 @@ -/* - * Copyright (C) 2020 Rerrah - * - * Permission is hereby granted, free of charge, to any person - * obtaining a copy of this software and associated documentation - * files (the "Software"), to deal in the Software without - * restriction, including without limitation the rights to use, - * copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following - * conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES - * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT - * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, - * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR - * OTHER DEALINGS IN THE SOFTWARE. - */ - -#include "paste_insert_copied_data_to_pattern_qt_command.hpp" -#include "command/command_id.hpp" - -PasteInsertCopiedDataToPatternQtCommand::PasteInsertCopiedDataToPatternQtCommand(PatternEditorPanel* panel, QUndoCommand* parent) - : QUndoCommand(parent), - panel_(panel) -{ -} - -void PasteInsertCopiedDataToPatternQtCommand::redo() -{ - panel_->redrawByPatternChanged(true); -} - -void PasteInsertCopiedDataToPatternQtCommand::undo() -{ - panel_->redrawByPatternChanged(true); -} - -int PasteInsertCopiedDataToPatternQtCommand::id() const -{ - return CommandId::PasteInsertCopiedDataToPattern; -} diff -Nru bambootracker-0.4.5+git20121209/BambooTracker/gui/command/pattern/paste_insert_copied_data_to_pattern_qt_command.hpp bambootracker-0.4.6/BambooTracker/gui/command/pattern/paste_insert_copied_data_to_pattern_qt_command.hpp --- bambootracker-0.4.5+git20121209/BambooTracker/gui/command/pattern/paste_insert_copied_data_to_pattern_qt_command.hpp 2020-12-08 14:05:28.000000000 +0000 +++ bambootracker-0.4.6/BambooTracker/gui/command/pattern/paste_insert_copied_data_to_pattern_qt_command.hpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,44 +0,0 @@ -/* - * Copyright (C) 2020 Rerrah - * - * Permission is hereby granted, free of charge, to any person - * obtaining a copy of this software and associated documentation - * files (the "Software"), to deal in the Software without - * restriction, including without limitation the rights to use, - * copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following - * conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES - * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT - * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, - * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR - * OTHER DEALINGS IN THE SOFTWARE. - */ - -#ifndef PASTE_INSERT_COPIED_DATA_TO_PATTERN_QT_COMMAND_HPP -#define PASTE_INSERT_COPIED_DATA_TO_PATTERN_QT_COMMAND_HPP - -#include -#include "gui/pattern_editor/pattern_editor_panel.hpp" - -class PasteInsertCopiedDataToPatternQtCommand : public QUndoCommand -{ -public: - PasteInsertCopiedDataToPatternQtCommand(PatternEditorPanel* panel, QUndoCommand* parent = nullptr); - void redo() Q_DECL_OVERRIDE; - void undo() Q_DECL_OVERRIDE; - int id() const Q_DECL_OVERRIDE; - -private: - PatternEditorPanel* panel_; -}; - -#endif // PASTE_INSERT_COPIED_DATA_TO_PATTERN_QT_COMMAND_HPP diff -Nru bambootracker-0.4.5+git20121209/BambooTracker/gui/command/pattern/paste_mix_copied_data_to_pattern_qt_command.cpp bambootracker-0.4.6/BambooTracker/gui/command/pattern/paste_mix_copied_data_to_pattern_qt_command.cpp --- bambootracker-0.4.5+git20121209/BambooTracker/gui/command/pattern/paste_mix_copied_data_to_pattern_qt_command.cpp 2020-12-08 14:05:28.000000000 +0000 +++ bambootracker-0.4.6/BambooTracker/gui/command/pattern/paste_mix_copied_data_to_pattern_qt_command.cpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,48 +0,0 @@ -/* - * Copyright (C) 2018-2019 Rerrah - * - * Permission is hereby granted, free of charge, to any person - * obtaining a copy of this software and associated documentation - * files (the "Software"), to deal in the Software without - * restriction, including without limitation the rights to use, - * copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following - * conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES - * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT - * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, - * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR - * OTHER DEALINGS IN THE SOFTWARE. - */ - -#include "paste_mix_copied_data_to_pattern_qt_command.hpp" -#include "command/command_id.hpp" - -PasteMixCopiedDataToPatternQtCommand::PasteMixCopiedDataToPatternQtCommand(PatternEditorPanel* panel, QUndoCommand* parent) - : QUndoCommand(parent), - panel_(panel) -{ -} - -void PasteMixCopiedDataToPatternQtCommand::redo() -{ - panel_->redrawByPatternChanged(true); -} - -void PasteMixCopiedDataToPatternQtCommand::undo() -{ - panel_->redrawByPatternChanged(true); -} - -int PasteMixCopiedDataToPatternQtCommand::id() const -{ - return CommandId::PasteMixCopiedDataToPattern; -} diff -Nru bambootracker-0.4.5+git20121209/BambooTracker/gui/command/pattern/paste_mix_copied_data_to_pattern_qt_command.hpp bambootracker-0.4.6/BambooTracker/gui/command/pattern/paste_mix_copied_data_to_pattern_qt_command.hpp --- bambootracker-0.4.5+git20121209/BambooTracker/gui/command/pattern/paste_mix_copied_data_to_pattern_qt_command.hpp 2020-12-08 14:05:28.000000000 +0000 +++ bambootracker-0.4.6/BambooTracker/gui/command/pattern/paste_mix_copied_data_to_pattern_qt_command.hpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,44 +0,0 @@ -/* - * Copyright (C) 2018 Rerrah - * - * Permission is hereby granted, free of charge, to any person - * obtaining a copy of this software and associated documentation - * files (the "Software"), to deal in the Software without - * restriction, including without limitation the rights to use, - * copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following - * conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES - * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT - * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, - * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR - * OTHER DEALINGS IN THE SOFTWARE. - */ - -#ifndef PASTE_MIX_COPIED_DATA_TO_PATTERN_QT_COMMAND_HPP -#define PASTE_MIX_COPIED_DATA_TO_PATTERN_QT_COMMAND_HPP - -#include -#include "gui/pattern_editor/pattern_editor_panel.hpp" - -class PasteMixCopiedDataToPatternQtCommand : public QUndoCommand -{ -public: - PasteMixCopiedDataToPatternQtCommand(PatternEditorPanel* panel, QUndoCommand* parent = nullptr); - void redo() Q_DECL_OVERRIDE; - void undo() Q_DECL_OVERRIDE; - int id() const Q_DECL_OVERRIDE; - -private: - PatternEditorPanel* panel_; -}; - -#endif // PASTE_MIX_COPIED_DATA_TO_PATTERN_QT_COMMAND_HPP diff -Nru bambootracker-0.4.5+git20121209/BambooTracker/gui/command/pattern/paste_overwrite_copied_data_to_pattern_qt_command.cpp bambootracker-0.4.6/BambooTracker/gui/command/pattern/paste_overwrite_copied_data_to_pattern_qt_command.cpp --- bambootracker-0.4.5+git20121209/BambooTracker/gui/command/pattern/paste_overwrite_copied_data_to_pattern_qt_command.cpp 2020-12-08 14:05:28.000000000 +0000 +++ bambootracker-0.4.6/BambooTracker/gui/command/pattern/paste_overwrite_copied_data_to_pattern_qt_command.cpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,48 +0,0 @@ -/* - * Copyright (C) 2018-2019 Rerrah - * - * Permission is hereby granted, free of charge, to any person - * obtaining a copy of this software and associated documentation - * files (the "Software"), to deal in the Software without - * restriction, including without limitation the rights to use, - * copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following - * conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES - * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT - * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, - * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR - * OTHER DEALINGS IN THE SOFTWARE. - */ - -#include "paste_overwrite_copied_data_to_pattern_qt_command.hpp" -#include "command/command_id.hpp" - -PasteOverwriteCopiedDataToPatternQtCommand::PasteOverwriteCopiedDataToPatternQtCommand(PatternEditorPanel* panel, QUndoCommand* parent) - : QUndoCommand(parent), - panel_(panel) -{ -} - -void PasteOverwriteCopiedDataToPatternQtCommand::redo() -{ - panel_->redrawByPatternChanged(true); -} - -void PasteOverwriteCopiedDataToPatternQtCommand::undo() -{ - panel_->redrawByPatternChanged(true); -} - -int PasteOverwriteCopiedDataToPatternQtCommand::id() const -{ - return CommandId::PasteOverwriteCopiedDataToPattern; -} diff -Nru bambootracker-0.4.5+git20121209/BambooTracker/gui/command/pattern/paste_overwrite_copied_data_to_pattern_qt_command.hpp bambootracker-0.4.6/BambooTracker/gui/command/pattern/paste_overwrite_copied_data_to_pattern_qt_command.hpp --- bambootracker-0.4.5+git20121209/BambooTracker/gui/command/pattern/paste_overwrite_copied_data_to_pattern_qt_command.hpp 2020-12-08 14:05:28.000000000 +0000 +++ bambootracker-0.4.6/BambooTracker/gui/command/pattern/paste_overwrite_copied_data_to_pattern_qt_command.hpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,44 +0,0 @@ -/* - * Copyright (C) 2018 Rerrah - * - * Permission is hereby granted, free of charge, to any person - * obtaining a copy of this software and associated documentation - * files (the "Software"), to deal in the Software without - * restriction, including without limitation the rights to use, - * copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following - * conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES - * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT - * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, - * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR - * OTHER DEALINGS IN THE SOFTWARE. - */ - -#ifndef PASTE_OVERWRITE_COPIED_DATA_TO_PATTERN_QT_COMMAND_HPP -#define PASTE_OVERWRITE_COPIED_DATA_TO_PATTERN_QT_COMMAND_HPP - -#include -#include "gui/pattern_editor/pattern_editor_panel.hpp" - -class PasteOverwriteCopiedDataToPatternQtCommand : public QUndoCommand -{ -public: - PasteOverwriteCopiedDataToPatternQtCommand(PatternEditorPanel* panel, QUndoCommand* parent = nullptr); - void redo() Q_DECL_OVERRIDE; - void undo() Q_DECL_OVERRIDE; - int id() const Q_DECL_OVERRIDE; - -private: - PatternEditorPanel* panel_; -}; - -#endif // PASTE_OVERWRITE_COPIED_DATA_TO_PATTERN_QT_COMMAND_HPP diff -Nru bambootracker-0.4.5+git20121209/BambooTracker/gui/command/pattern/pattern_commands_qt.hpp bambootracker-0.4.6/BambooTracker/gui/command/pattern/pattern_commands_qt.hpp --- bambootracker-0.4.5+git20121209/BambooTracker/gui/command/pattern/pattern_commands_qt.hpp 2020-12-08 14:05:28.000000000 +0000 +++ bambootracker-0.4.6/BambooTracker/gui/command/pattern/pattern_commands_qt.hpp 2021-02-11 14:37:17.000000000 +0000 @@ -26,32 +26,33 @@ #ifndef PATTERN_COMMANDS_QT_HPP #define PATTERN_COMMANDS_QT_HPP -/********** Pattern edit **********/ -#include "set_key_on_to_step_qt_command.hpp" -#include "set_key_off_to_step_qt_command.hpp" -#include "erase_step_qt_command.hpp" -#include "set_instrument_to_step_qt_command.hpp" -#include "erase_instrument_in_step_qt_command.hpp" -#include "set_volume_to_step_qt_command.hpp" -#include "erase_volume_in_step_qt_command.hpp" -#include "set_effect_id_to_step_qt_command.hpp" -#include "erase_effect_in_step_qt_command.hpp" -#include "set_effect_value_to_step_qt_command.hpp" -#include "erase_effect_value_in_step_qt_command.hpp" -#include "insert_step_qt_command.hpp" -#include "delete_previous_step_qt_command.hpp" -#include "paste_copied_data_to_pattern_qt_command.hpp" -#include "erase_cells_in_pattern_qt_command.hpp" -#include "paste_mix_copied_data_to_pattern_qt_command.hpp" -#include "transpose_note_in_pattern_qt_command.hpp" -#include "expand_pattern_qt_command.hpp" -#include "shrink_pattern_qt_command.hpp" -#include "set_echo_buffer_access_qt_command.hpp" -#include "interpolate_pattern_qt_command.hpp" -#include "reverse_pattern_qt_command.hpp" -#include "replace_instrument_in_pattern_qt_command.hpp" -#include "paste_overwrite_copied_data_to_pattern_qt_command.hpp" -#include "change_values_in_pattern_qt_command.hpp" -#include "paste_insert_copied_data_to_pattern_qt_command.hpp" +#include "pattern_editor_common_qt_command.hpp" + +using ChangeValuesInPatternQtCommand = gui_command_impl::PatternEditorCommonQtCommandRedrawAll; +using DeletePreviousStepQtCommand = gui_command_impl::PatternEditorCommonQtCommandRedrawAll; +using EraseCellsInPatternQtCommand = gui_command_impl::PatternEditorCommonQtCommandRedrawAll; +using EraseEffectInStepQtCommand = gui_command_impl::PatternEditorCommonQtCommandRedrawAll; +using EraseEffectValueInStepQtCommand = gui_command_impl::PatternEditorCommonQtCommandRedrawAll; +using EraseInstrumentInStepQtCommand = gui_command_impl::PatternEditorCommonQtCommandRedrawText; +using EraseStepQtCommand = gui_command_impl::PatternEditorCommonQtCommandRedrawAll; +using EraseVolumeInStepQtCommand = gui_command_impl::PatternEditorCommonQtCommandRedrawText; +using ExpandPatternQtCommand = gui_command_impl::PatternEditorCommonQtCommandRedrawAll; +using InsertStepQtCommand = gui_command_impl::PatternEditorCommonQtCommandRedrawAll; +using InterpolatePatternQtCommand = gui_command_impl::PatternEditorCommonQtCommandRedrawText; +using PasteCopiedDataToPatternQtCommand = gui_command_impl::PatternEditorCommonQtCommandRedrawAll; +using PasteInsertCopiedDataToPatternQtCommand = gui_command_impl::PatternEditorCommonQtCommandRedrawAll; +using PasteMixCopiedDataToPatternQtCommand = gui_command_impl::PatternEditorCommonQtCommandRedrawAll; +using PasteOverwriteCopiedDataToPatternQtCommand = gui_command_impl::PatternEditorCommonQtCommandRedrawAll; +using ReplaceInstrumentInPatternQtCommand = gui_command_impl::PatternEditorCommonQtCommandRedrawText; +using ReversePatternQtCommand = gui_command_impl::PatternEditorCommonQtCommandRedrawAll; +using SetEchoBufferAccessQtCommand = gui_command_impl::PatternEditorCommonQtCommandRedrawText; +using SetEffectIDToStepQtCommand = gui_command_impl::PatternEditorEntryQtCommandRedrawAll; +using SetEffectValueToStepQtCommand = gui_command_impl::PatternEditorEntryQtCommandRedrawText; +using SetInstrumentToStepQtCommand = gui_command_impl::PatternEditorEntryQtCommandRedrawText; +using SetKeyOffToStepQtCommand = gui_command_impl::PatternEditorCommonQtCommandRedrawText; +using SetKeyOnToStepQtCommand = gui_command_impl::PatternEditorCommonQtCommandRedrawText; +using SetVolumeToStepQtCommand = gui_command_impl::PatternEditorEntryQtCommandRedrawText; +using ShrinkPatternQtCommand = gui_command_impl::PatternEditorCommonQtCommandRedrawAll; +using TransposeNoteInPatternQtCommand = gui_command_impl::PatternEditorCommonQtCommandRedrawText; #endif // PATTERN_COMMANDS_QT_HPP diff -Nru bambootracker-0.4.5+git20121209/BambooTracker/gui/command/pattern/pattern_editor_common_qt_command.cpp bambootracker-0.4.6/BambooTracker/gui/command/pattern/pattern_editor_common_qt_command.cpp --- bambootracker-0.4.5+git20121209/BambooTracker/gui/command/pattern/pattern_editor_common_qt_command.cpp 1970-01-01 00:00:00.000000000 +0000 +++ bambootracker-0.4.6/BambooTracker/gui/command/pattern/pattern_editor_common_qt_command.cpp 2021-02-11 14:37:17.000000000 +0000 @@ -0,0 +1,81 @@ +/* + * Copyright (C) 2020 Rerrah + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +#include "pattern_editor_common_qt_command.hpp" +#include "gui/pattern_editor/pattern_editor_panel.hpp" + +namespace gui_command_impl +{ +PatternEditorCommonQtCommand::PatternEditorCommonQtCommand( + CommandId id, PatternEditorPanel* panel, bool redrawAll, QUndoCommand* parent) + : QUndoCommand(parent), panel_(panel), id_(id), redrawAll_(redrawAll) +{ +} + +void PatternEditorCommonQtCommand::redo() +{ + panel_->redrawByPatternChanged(redrawAll_); +} + +void PatternEditorCommonQtCommand::undo() +{ + panel_->redrawByPatternChanged(redrawAll_); +} + +int PatternEditorCommonQtCommand::id() const +{ + return id_; +} + +PatternEditorEntryQtCommand::PatternEditorEntryQtCommand( + CommandId id, PatternEditorPanel* panel, bool redrawAll, const PatternPosition& pos, + bool secondEntry, QUndoCommand* parent) + : PatternEditorCommonQtCommand(id, panel, redrawAll, parent), + pos_(pos), + isSecondEntry_(secondEntry) +{ +} + +void PatternEditorEntryQtCommand::undo() +{ + PatternEditorCommonQtCommand::undo(); + panel_->resetEntryCount(); +} + +bool PatternEditorEntryQtCommand::mergeWith(const QUndoCommand* other) +{ + if (other->id() == id() && !isSecondEntry_) { + auto com = dynamic_cast(other); + if (com->pos_ == pos_ && com->isSecondEntry_) { + isSecondEntry_ = true; + redo(); + return true; + } + } + + isSecondEntry_ = true; + return false; +} +} diff -Nru bambootracker-0.4.5+git20121209/BambooTracker/gui/command/pattern/pattern_editor_common_qt_command.hpp bambootracker-0.4.6/BambooTracker/gui/command/pattern/pattern_editor_common_qt_command.hpp --- bambootracker-0.4.5+git20121209/BambooTracker/gui/command/pattern/pattern_editor_common_qt_command.hpp 1970-01-01 00:00:00.000000000 +0000 +++ bambootracker-0.4.6/BambooTracker/gui/command/pattern/pattern_editor_common_qt_command.hpp 2021-02-11 14:37:17.000000000 +0000 @@ -0,0 +1,99 @@ +/* + * Copyright (C) 2020 Rerrah + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +#ifndef PATTERN_EDITOR_COMMON_QT_COMMAND_HPP +#define PATTERN_EDITOR_COMMON_QT_COMMAND_HPP + +#include +#include "command/command_id.hpp" +#include "gui/pattern_editor/pattern_position.hpp" + +class PatternEditorPanel; + +namespace gui_command_impl +{ +class PatternEditorCommonQtCommand : public QUndoCommand +{ +public: + virtual void redo() override; + virtual void undo() override; + int id() const override final; + +protected: + PatternEditorPanel* panel_; + PatternEditorCommonQtCommand(CommandId id, PatternEditorPanel* panel, + bool redrawAll, QUndoCommand* parent); + +private: + CommandId id_; + bool redrawAll_; +}; + +template +class PatternEditorCommonQtCommandRedraw final : public PatternEditorCommonQtCommand +{ +public: + PatternEditorCommonQtCommandRedraw(PatternEditorPanel* panel, QUndoCommand* parent = nullptr) + : PatternEditorCommonQtCommand(comId, panel, redrawAll, parent) {} +}; + +template +using PatternEditorCommonQtCommandRedrawAll = PatternEditorCommonQtCommandRedraw; + +template +using PatternEditorCommonQtCommandRedrawText = PatternEditorCommonQtCommandRedraw; + +class PatternEditorEntryQtCommand : public PatternEditorCommonQtCommand +{ +public: + void undo() override; + bool mergeWith(const QUndoCommand* other) override; + +protected: + PatternEditorEntryQtCommand(CommandId id, PatternEditorPanel* panel, bool redrawAll, + const PatternPosition& pos, bool secondEntry, QUndoCommand* parent); + +private: + const PatternPosition pos_; + bool isSecondEntry_; +}; + +template +class PatternEditorEntryQtCommandRedraw final : public PatternEditorEntryQtCommand +{ +public: + PatternEditorEntryQtCommandRedraw(PatternEditorPanel* panel, const PatternPosition& pos, + bool secondEntry, QUndoCommand* parent = nullptr) + : PatternEditorEntryQtCommand(comId, panel, redrawAll, pos, secondEntry, parent) {} +}; + +template +using PatternEditorEntryQtCommandRedrawAll = PatternEditorEntryQtCommandRedraw; + +template +using PatternEditorEntryQtCommandRedrawText = PatternEditorEntryQtCommandRedraw; +} + +#endif // PATTERN_EDITOR_COMMON_QT_COMMAND_HPP diff -Nru bambootracker-0.4.5+git20121209/BambooTracker/gui/command/pattern/replace_instrument_in_pattern_qt_command.cpp bambootracker-0.4.6/BambooTracker/gui/command/pattern/replace_instrument_in_pattern_qt_command.cpp --- bambootracker-0.4.5+git20121209/BambooTracker/gui/command/pattern/replace_instrument_in_pattern_qt_command.cpp 2020-12-08 14:05:28.000000000 +0000 +++ bambootracker-0.4.6/BambooTracker/gui/command/pattern/replace_instrument_in_pattern_qt_command.cpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,48 +0,0 @@ -/* - * Copyright (C) 2018-2019 Rerrah - * - * Permission is hereby granted, free of charge, to any person - * obtaining a copy of this software and associated documentation - * files (the "Software"), to deal in the Software without - * restriction, including without limitation the rights to use, - * copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following - * conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES - * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT - * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, - * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR - * OTHER DEALINGS IN THE SOFTWARE. - */ - -#include "replace_instrument_in_pattern_qt_command.hpp" -#include "command/command_id.hpp" - -ReplaceInstrumentInPatternQtCommand::ReplaceInstrumentInPatternQtCommand(PatternEditorPanel* panel, QUndoCommand* parent) - : QUndoCommand(parent), - panel_(panel) -{ -} - -void ReplaceInstrumentInPatternQtCommand::redo() -{ - panel_->redrawByPatternChanged(); -} - -void ReplaceInstrumentInPatternQtCommand::undo() -{ - panel_->redrawByPatternChanged(); -} - -int ReplaceInstrumentInPatternQtCommand::id() const -{ - return CommandId::ReplaceInstrumentInPattern; -} diff -Nru bambootracker-0.4.5+git20121209/BambooTracker/gui/command/pattern/replace_instrument_in_pattern_qt_command.hpp bambootracker-0.4.6/BambooTracker/gui/command/pattern/replace_instrument_in_pattern_qt_command.hpp --- bambootracker-0.4.5+git20121209/BambooTracker/gui/command/pattern/replace_instrument_in_pattern_qt_command.hpp 2020-12-08 14:05:28.000000000 +0000 +++ bambootracker-0.4.6/BambooTracker/gui/command/pattern/replace_instrument_in_pattern_qt_command.hpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,44 +0,0 @@ -/* - * Copyright (C) 2018 Rerrah - * - * Permission is hereby granted, free of charge, to any person - * obtaining a copy of this software and associated documentation - * files (the "Software"), to deal in the Software without - * restriction, including without limitation the rights to use, - * copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following - * conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES - * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT - * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, - * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR - * OTHER DEALINGS IN THE SOFTWARE. - */ - -#ifndef REPLACE_INSTRUMENT_IN_PATTERN_QT_COMMAND_HPP -#define REPLACE_INSTRUMENT_IN_PATTERN_QT_COMMAND_HPP - -#include -#include "gui/pattern_editor/pattern_editor_panel.hpp" - -class ReplaceInstrumentInPatternQtCommand : public QUndoCommand -{ -public: - ReplaceInstrumentInPatternQtCommand(PatternEditorPanel* panel, QUndoCommand* parent = nullptr); - void redo() Q_DECL_OVERRIDE; - void undo() Q_DECL_OVERRIDE; - int id() const Q_DECL_OVERRIDE; - -private: - PatternEditorPanel* panel_; -}; - -#endif // REPLACE_INSTRUMENT_IN_PATTERN_QT_COMMAND_HPP diff -Nru bambootracker-0.4.5+git20121209/BambooTracker/gui/command/pattern/reverse_pattern_qt_command.cpp bambootracker-0.4.6/BambooTracker/gui/command/pattern/reverse_pattern_qt_command.cpp --- bambootracker-0.4.5+git20121209/BambooTracker/gui/command/pattern/reverse_pattern_qt_command.cpp 2020-12-08 14:05:28.000000000 +0000 +++ bambootracker-0.4.6/BambooTracker/gui/command/pattern/reverse_pattern_qt_command.cpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,48 +0,0 @@ -/* - * Copyright (C) 2018-2019 Rerrah - * - * Permission is hereby granted, free of charge, to any person - * obtaining a copy of this software and associated documentation - * files (the "Software"), to deal in the Software without - * restriction, including without limitation the rights to use, - * copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following - * conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES - * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT - * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, - * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR - * OTHER DEALINGS IN THE SOFTWARE. - */ - -#include "reverse_pattern_qt_command.hpp" -#include "command/command_id.hpp" - -ReversePatternQtCommand::ReversePatternQtCommand(PatternEditorPanel* panel, QUndoCommand* parent) - : QUndoCommand(parent), - panel_(panel) -{ -} - -void ReversePatternQtCommand::redo() -{ - panel_->redrawByPatternChanged(true); -} - -void ReversePatternQtCommand::undo() -{ - panel_->redrawByPatternChanged(true); -} - -int ReversePatternQtCommand::id() const -{ - return CommandId::ReversePattern; -} diff -Nru bambootracker-0.4.5+git20121209/BambooTracker/gui/command/pattern/reverse_pattern_qt_command.hpp bambootracker-0.4.6/BambooTracker/gui/command/pattern/reverse_pattern_qt_command.hpp --- bambootracker-0.4.5+git20121209/BambooTracker/gui/command/pattern/reverse_pattern_qt_command.hpp 2020-12-08 14:05:28.000000000 +0000 +++ bambootracker-0.4.6/BambooTracker/gui/command/pattern/reverse_pattern_qt_command.hpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,44 +0,0 @@ -/* - * Copyright (C) 2018 Rerrah - * - * Permission is hereby granted, free of charge, to any person - * obtaining a copy of this software and associated documentation - * files (the "Software"), to deal in the Software without - * restriction, including without limitation the rights to use, - * copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following - * conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES - * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT - * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, - * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR - * OTHER DEALINGS IN THE SOFTWARE. - */ - -#ifndef REVERSE_PATTERN_QT_COMMAND_HPP -#define REVERSE_PATTERN_QT_COMMAND_HPP - -#include -#include "gui/pattern_editor/pattern_editor_panel.hpp" - -class ReversePatternQtCommand : public QUndoCommand -{ -public: - ReversePatternQtCommand(PatternEditorPanel* panel, QUndoCommand* parent = nullptr); - void redo() Q_DECL_OVERRIDE; - void undo() Q_DECL_OVERRIDE; - int id() const Q_DECL_OVERRIDE; - -private: - PatternEditorPanel* panel_; -}; - -#endif // REVERSE_PATTERN_QT_COMMAND_HPP diff -Nru bambootracker-0.4.5+git20121209/BambooTracker/gui/command/pattern/set_echo_buffer_access_qt_command.cpp bambootracker-0.4.6/BambooTracker/gui/command/pattern/set_echo_buffer_access_qt_command.cpp --- bambootracker-0.4.5+git20121209/BambooTracker/gui/command/pattern/set_echo_buffer_access_qt_command.cpp 2020-12-08 14:05:28.000000000 +0000 +++ bambootracker-0.4.6/BambooTracker/gui/command/pattern/set_echo_buffer_access_qt_command.cpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,48 +0,0 @@ -/* - * Copyright (C) 2018-2019 Rerrah - * - * Permission is hereby granted, free of charge, to any person - * obtaining a copy of this software and associated documentation - * files (the "Software"), to deal in the Software without - * restriction, including without limitation the rights to use, - * copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following - * conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES - * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT - * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, - * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR - * OTHER DEALINGS IN THE SOFTWARE. - */ - -#include "set_echo_buffer_access_qt_command.hpp" -#include "command/command_id.hpp" - -SetEchoBufferAccessQtCommand::SetEchoBufferAccessQtCommand(PatternEditorPanel* panel, QUndoCommand* parent) - : QUndoCommand(parent), - panel_(panel) -{ -} - -void SetEchoBufferAccessQtCommand::redo() -{ - panel_->redrawByPatternChanged(); -} - -void SetEchoBufferAccessQtCommand::undo() -{ - panel_->redrawByPatternChanged(); -} - -int SetEchoBufferAccessQtCommand::id() const -{ - return CommandId::SetEchoBufferAccess; -} diff -Nru bambootracker-0.4.5+git20121209/BambooTracker/gui/command/pattern/set_echo_buffer_access_qt_command.hpp bambootracker-0.4.6/BambooTracker/gui/command/pattern/set_echo_buffer_access_qt_command.hpp --- bambootracker-0.4.5+git20121209/BambooTracker/gui/command/pattern/set_echo_buffer_access_qt_command.hpp 2020-12-08 14:05:28.000000000 +0000 +++ bambootracker-0.4.6/BambooTracker/gui/command/pattern/set_echo_buffer_access_qt_command.hpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,45 +0,0 @@ -/* - * Copyright (C) 2018 Rerrah - * - * Permission is hereby granted, free of charge, to any person - * obtaining a copy of this software and associated documentation - * files (the "Software"), to deal in the Software without - * restriction, including without limitation the rights to use, - * copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following - * conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES - * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT - * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, - * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR - * OTHER DEALINGS IN THE SOFTWARE. - */ - -#ifndef SET_ECHO_BUFFER_ACCESS_QT_COMMAND_HPP -#define SET_ECHO_BUFFER_ACCESS_QT_COMMAND_HPP - -#include -#include "gui/pattern_editor/pattern_editor_panel.hpp" - - -class SetEchoBufferAccessQtCommand : public QUndoCommand -{ -public: - SetEchoBufferAccessQtCommand(PatternEditorPanel* panel, QUndoCommand* parent = nullptr); - void redo() Q_DECL_OVERRIDE; - void undo() Q_DECL_OVERRIDE; - int id() const Q_DECL_OVERRIDE; - -private: - PatternEditorPanel* panel_; -}; - -#endif // SET_ECHO_BUFFER_ACCESS_QT_COMMAND_HPP diff -Nru bambootracker-0.4.5+git20121209/BambooTracker/gui/command/pattern/set_effect_id_to_step_qt_command.cpp bambootracker-0.4.6/BambooTracker/gui/command/pattern/set_effect_id_to_step_qt_command.cpp --- bambootracker-0.4.5+git20121209/BambooTracker/gui/command/pattern/set_effect_id_to_step_qt_command.cpp 2020-12-08 14:05:28.000000000 +0000 +++ bambootracker-0.4.6/BambooTracker/gui/command/pattern/set_effect_id_to_step_qt_command.cpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,66 +0,0 @@ -/* - * Copyright (C) 2018-2020 Rerrah - * - * Permission is hereby granted, free of charge, to any person - * obtaining a copy of this software and associated documentation - * files (the "Software"), to deal in the Software without - * restriction, including without limitation the rights to use, - * copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following - * conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES - * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT - * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, - * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR - * OTHER DEALINGS IN THE SOFTWARE. - */ - -#include "set_effect_id_to_step_qt_command.hpp" -#include "command/command_id.hpp" - -SetEffectIDToStepQtCommand::SetEffectIDToStepQtCommand(PatternEditorPanel* panel, PatternPosition pos, bool secondEntry, QUndoCommand* parent) - : QUndoCommand(parent), - panel_(panel), - pos_(pos), - isSecondEntry_(secondEntry) -{ -} - -void SetEffectIDToStepQtCommand::redo() -{ - panel_->redrawByPatternChanged(true); -} - -void SetEffectIDToStepQtCommand::undo() -{ - panel_->redrawByPatternChanged(true); - panel_->resetEntryCount(); -} - -int SetEffectIDToStepQtCommand::id() const -{ - return CommandId::SetEffectIDToStep; -} - -bool SetEffectIDToStepQtCommand::mergeWith(const QUndoCommand* other) -{ - if (other->id() == id() && !isSecondEntry_) { - auto com = dynamic_cast(other); - if (com->pos_ == pos_ && com->isSecondEntry_) { - isSecondEntry_ = true; - redo(); - return true; - } - } - - isSecondEntry_ = true; - return false; -} diff -Nru bambootracker-0.4.5+git20121209/BambooTracker/gui/command/pattern/set_effect_id_to_step_qt_command.hpp bambootracker-0.4.6/BambooTracker/gui/command/pattern/set_effect_id_to_step_qt_command.hpp --- bambootracker-0.4.5+git20121209/BambooTracker/gui/command/pattern/set_effect_id_to_step_qt_command.hpp 2020-12-08 14:05:28.000000000 +0000 +++ bambootracker-0.4.6/BambooTracker/gui/command/pattern/set_effect_id_to_step_qt_command.hpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,48 +0,0 @@ -/* - * Copyright (C) 2018-2020 Rerrah - * - * Permission is hereby granted, free of charge, to any person - * obtaining a copy of this software and associated documentation - * files (the "Software"), to deal in the Software without - * restriction, including without limitation the rights to use, - * copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following - * conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES - * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT - * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, - * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR - * OTHER DEALINGS IN THE SOFTWARE. - */ - -#ifndef SET_EFFECT_ID_TO_STEP_QT_COMMAND_HPP -#define SET_EFFECT_ID_TO_STEP_QT_COMMAND_HPP - -#include -#include "gui/pattern_editor/pattern_editor_panel.hpp" -#include "gui/pattern_editor/pattern_position.hpp" - -class SetEffectIDToStepQtCommand : public QUndoCommand -{ -public: - SetEffectIDToStepQtCommand(PatternEditorPanel* panel, PatternPosition pos, bool secondEntry, QUndoCommand* parent = nullptr); - void redo() Q_DECL_OVERRIDE; - void undo() Q_DECL_OVERRIDE; - int id() const Q_DECL_OVERRIDE; - bool mergeWith(const QUndoCommand* other) Q_DECL_OVERRIDE; - -private: - PatternEditorPanel* panel_; - const PatternPosition pos_; - bool isSecondEntry_; -}; - -#endif // SET_EFFECT_ID_TO_STEP_QT_COMMAND_HPP diff -Nru bambootracker-0.4.5+git20121209/BambooTracker/gui/command/pattern/set_effect_value_to_step_qt_command.cpp bambootracker-0.4.6/BambooTracker/gui/command/pattern/set_effect_value_to_step_qt_command.cpp --- bambootracker-0.4.5+git20121209/BambooTracker/gui/command/pattern/set_effect_value_to_step_qt_command.cpp 2020-12-08 14:05:28.000000000 +0000 +++ bambootracker-0.4.6/BambooTracker/gui/command/pattern/set_effect_value_to_step_qt_command.cpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,66 +0,0 @@ -/* - * Copyright (C) 2018-2020 Rerrah - * - * Permission is hereby granted, free of charge, to any person - * obtaining a copy of this software and associated documentation - * files (the "Software"), to deal in the Software without - * restriction, including without limitation the rights to use, - * copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following - * conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES - * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT - * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, - * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR - * OTHER DEALINGS IN THE SOFTWARE. - */ - -#include "set_effect_value_to_step_qt_command.hpp" -#include "command/command_id.hpp" - -SetEffectValueToStepQtCommand::SetEffectValueToStepQtCommand(PatternEditorPanel* panel, PatternPosition pos, bool secondEntry, QUndoCommand* parent) - : QUndoCommand(parent), - panel_(panel), - pos_(pos), - isSecondEntry_(secondEntry) -{ -} - -void SetEffectValueToStepQtCommand::redo() -{ - panel_->redrawByPatternChanged(); -} - -void SetEffectValueToStepQtCommand::undo() -{ - panel_->redrawByPatternChanged(); - panel_->resetEntryCount(); -} - -int SetEffectValueToStepQtCommand::id() const -{ - return CommandId::SetEffectValueToStep; -} - -bool SetEffectValueToStepQtCommand::mergeWith(const QUndoCommand* other) -{ - if (other->id() == id() && !isSecondEntry_) { - auto com = dynamic_cast(other); - if (com->pos_ == pos_ && com->isSecondEntry_) { - isSecondEntry_ = true; - redo(); - return true; - } - } - - isSecondEntry_ = true; - return false; -} diff -Nru bambootracker-0.4.5+git20121209/BambooTracker/gui/command/pattern/set_effect_value_to_step_qt_command.hpp bambootracker-0.4.6/BambooTracker/gui/command/pattern/set_effect_value_to_step_qt_command.hpp --- bambootracker-0.4.5+git20121209/BambooTracker/gui/command/pattern/set_effect_value_to_step_qt_command.hpp 2020-12-08 14:05:28.000000000 +0000 +++ bambootracker-0.4.6/BambooTracker/gui/command/pattern/set_effect_value_to_step_qt_command.hpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,48 +0,0 @@ -/* - * Copyright (C) 2018-2020 Rerrah - * - * Permission is hereby granted, free of charge, to any person - * obtaining a copy of this software and associated documentation - * files (the "Software"), to deal in the Software without - * restriction, including without limitation the rights to use, - * copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following - * conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES - * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT - * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, - * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR - * OTHER DEALINGS IN THE SOFTWARE. - */ - -#ifndef SET_EFFECT_VALUE_TO_STEP_QT_COMMAND_HPP -#define SET_EFFECT_VALUE_TO_STEP_QT_COMMAND_HPP - -#include -#include "gui/pattern_editor/pattern_editor_panel.hpp" -#include "gui/pattern_editor/pattern_position.hpp" - -class SetEffectValueToStepQtCommand : public QUndoCommand -{ -public: - SetEffectValueToStepQtCommand(PatternEditorPanel* panel, PatternPosition pos, bool secondEntry, QUndoCommand* parent = nullptr); - void redo() Q_DECL_OVERRIDE; - void undo() Q_DECL_OVERRIDE; - int id() const Q_DECL_OVERRIDE; - bool mergeWith(const QUndoCommand* other) Q_DECL_OVERRIDE; - -private: - PatternEditorPanel* panel_; - const PatternPosition pos_; - bool isSecondEntry_; -}; - -#endif // SET_EFFECT_VALUE_TO_STEP_QT_COMMAND_HPP diff -Nru bambootracker-0.4.5+git20121209/BambooTracker/gui/command/pattern/set_instrument_to_step_qt_command.cpp bambootracker-0.4.6/BambooTracker/gui/command/pattern/set_instrument_to_step_qt_command.cpp --- bambootracker-0.4.5+git20121209/BambooTracker/gui/command/pattern/set_instrument_to_step_qt_command.cpp 2020-12-08 14:05:28.000000000 +0000 +++ bambootracker-0.4.6/BambooTracker/gui/command/pattern/set_instrument_to_step_qt_command.cpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,66 +0,0 @@ -/* - * Copyright (C) 2018-2020 Rerrah - * - * Permission is hereby granted, free of charge, to any person - * obtaining a copy of this software and associated documentation - * files (the "Software"), to deal in the Software without - * restriction, including without limitation the rights to use, - * copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following - * conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES - * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT - * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, - * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR - * OTHER DEALINGS IN THE SOFTWARE. - */ - -#include "set_instrument_to_step_qt_command.hpp" -#include "command/command_id.hpp" - -SetInstrumentToStepQtCommand::SetInstrumentToStepQtCommand(PatternEditorPanel* panel, PatternPosition pos, bool secondEntry, QUndoCommand* parent) - : QUndoCommand(parent), - panel_(panel), - pos_(pos), - isSecondEntry_(secondEntry) -{ -} - -void SetInstrumentToStepQtCommand::redo() -{ - panel_->redrawByPatternChanged(); -} - -void SetInstrumentToStepQtCommand::undo() -{ - panel_->redrawByPatternChanged(); - panel_->resetEntryCount(); -} - -int SetInstrumentToStepQtCommand::id() const -{ - return CommandId::SetInstrumentInStep; -} - -bool SetInstrumentToStepQtCommand::mergeWith(const QUndoCommand* other) -{ - if (other->id() == id() && !isSecondEntry_) { - auto com = dynamic_cast(other); - if (com->pos_ == pos_ && com->isSecondEntry_) { - isSecondEntry_ = true; - redo(); - return true; - } - } - - isSecondEntry_ = true; - return false; -} diff -Nru bambootracker-0.4.5+git20121209/BambooTracker/gui/command/pattern/set_instrument_to_step_qt_command.hpp bambootracker-0.4.6/BambooTracker/gui/command/pattern/set_instrument_to_step_qt_command.hpp --- bambootracker-0.4.5+git20121209/BambooTracker/gui/command/pattern/set_instrument_to_step_qt_command.hpp 2020-12-08 14:05:28.000000000 +0000 +++ bambootracker-0.4.6/BambooTracker/gui/command/pattern/set_instrument_to_step_qt_command.hpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,48 +0,0 @@ -/* - * Copyright (C) 2018-2020 Rerrah - * - * Permission is hereby granted, free of charge, to any person - * obtaining a copy of this software and associated documentation - * files (the "Software"), to deal in the Software without - * restriction, including without limitation the rights to use, - * copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following - * conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES - * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT - * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, - * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR - * OTHER DEALINGS IN THE SOFTWARE. - */ - -#ifndef SET_INSTRUMENT_TO_STEP_QT_COMMAND_HPP -#define SET_INSTRUMENT_TO_STEP_QT_COMMAND_HPP - -#include -#include "gui/pattern_editor/pattern_editor_panel.hpp" -#include "gui/pattern_editor/pattern_position.hpp" - -class SetInstrumentToStepQtCommand : public QUndoCommand -{ -public: - SetInstrumentToStepQtCommand(PatternEditorPanel* panel, PatternPosition pos, bool secondEntry, QUndoCommand* parent = nullptr); - void redo() Q_DECL_OVERRIDE; - void undo() Q_DECL_OVERRIDE; - int id() const Q_DECL_OVERRIDE; - bool mergeWith(const QUndoCommand* other) Q_DECL_OVERRIDE; - -private: - PatternEditorPanel* panel_; - const PatternPosition pos_; - bool isSecondEntry_; -}; - -#endif // SET_INSTRUMENT_TO_STEP_QT_COMMAND_HPP diff -Nru bambootracker-0.4.5+git20121209/BambooTracker/gui/command/pattern/set_key_off_to_step_qt_command.cpp bambootracker-0.4.6/BambooTracker/gui/command/pattern/set_key_off_to_step_qt_command.cpp --- bambootracker-0.4.5+git20121209/BambooTracker/gui/command/pattern/set_key_off_to_step_qt_command.cpp 2020-12-08 14:05:28.000000000 +0000 +++ bambootracker-0.4.6/BambooTracker/gui/command/pattern/set_key_off_to_step_qt_command.cpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,48 +0,0 @@ -/* - * Copyright (C) 2018-2019 Rerrah - * - * Permission is hereby granted, free of charge, to any person - * obtaining a copy of this software and associated documentation - * files (the "Software"), to deal in the Software without - * restriction, including without limitation the rights to use, - * copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following - * conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES - * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT - * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, - * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR - * OTHER DEALINGS IN THE SOFTWARE. - */ - -#include "set_key_off_to_step_qt_command.hpp" -#include "command/command_id.hpp" - -SetKeyOffToStepQtCommand::SetKeyOffToStepQtCommand(PatternEditorPanel* panel, QUndoCommand* parent) - : QUndoCommand(parent), - panel_(panel) -{ -} - -void SetKeyOffToStepQtCommand::redo() -{ - panel_->redrawByPatternChanged(); -} - -void SetKeyOffToStepQtCommand::undo() -{ - panel_->redrawByPatternChanged(); -} - -int SetKeyOffToStepQtCommand::id() const -{ - return CommandId::SetKeyOffToStep; -} diff -Nru bambootracker-0.4.5+git20121209/BambooTracker/gui/command/pattern/set_key_off_to_step_qt_command.hpp bambootracker-0.4.6/BambooTracker/gui/command/pattern/set_key_off_to_step_qt_command.hpp --- bambootracker-0.4.5+git20121209/BambooTracker/gui/command/pattern/set_key_off_to_step_qt_command.hpp 2020-12-08 14:05:28.000000000 +0000 +++ bambootracker-0.4.6/BambooTracker/gui/command/pattern/set_key_off_to_step_qt_command.hpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,44 +0,0 @@ -/* - * Copyright (C) 2018 Rerrah - * - * Permission is hereby granted, free of charge, to any person - * obtaining a copy of this software and associated documentation - * files (the "Software"), to deal in the Software without - * restriction, including without limitation the rights to use, - * copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following - * conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES - * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT - * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, - * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR - * OTHER DEALINGS IN THE SOFTWARE. - */ - -#ifndef SET_KEY_OFF_TO_STEP_QT_COMMAND_HPP -#define SET_KEY_OFF_TO_STEP_QT_COMMAND_HPP - -#include -#include "gui/pattern_editor/pattern_editor_panel.hpp" - -class SetKeyOffToStepQtCommand : public QUndoCommand -{ -public: - SetKeyOffToStepQtCommand(PatternEditorPanel* panel, QUndoCommand* parent = nullptr); - void redo() Q_DECL_OVERRIDE; - void undo() Q_DECL_OVERRIDE; - int id() const Q_DECL_OVERRIDE; - -private: - PatternEditorPanel* panel_; -}; - -#endif // SET_KEY_OFF_TO_STEP_QT_COMMAND_HPP diff -Nru bambootracker-0.4.5+git20121209/BambooTracker/gui/command/pattern/set_key_on_to_step_qt_command.cpp bambootracker-0.4.6/BambooTracker/gui/command/pattern/set_key_on_to_step_qt_command.cpp --- bambootracker-0.4.5+git20121209/BambooTracker/gui/command/pattern/set_key_on_to_step_qt_command.cpp 2020-12-08 14:05:28.000000000 +0000 +++ bambootracker-0.4.6/BambooTracker/gui/command/pattern/set_key_on_to_step_qt_command.cpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,48 +0,0 @@ -/* - * Copyright (C) 2018-2019 Rerrah - * - * Permission is hereby granted, free of charge, to any person - * obtaining a copy of this software and associated documentation - * files (the "Software"), to deal in the Software without - * restriction, including without limitation the rights to use, - * copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following - * conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES - * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT - * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, - * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR - * OTHER DEALINGS IN THE SOFTWARE. - */ - -#include "set_key_on_to_step_qt_command.hpp" -#include "command/command_id.hpp" - -SetKeyOnToStepQtCommand::SetKeyOnToStepQtCommand(PatternEditorPanel* panel, QUndoCommand* parent) - : QUndoCommand(parent), - panel_(panel) -{ -} - -void SetKeyOnToStepQtCommand::redo() -{ - panel_->redrawByPatternChanged(); -} - -void SetKeyOnToStepQtCommand::undo() -{ - panel_->redrawByPatternChanged(); -} - -int SetKeyOnToStepQtCommand::id() const -{ - return CommandId::SetKeyOnToStep; -} diff -Nru bambootracker-0.4.5+git20121209/BambooTracker/gui/command/pattern/set_key_on_to_step_qt_command.hpp bambootracker-0.4.6/BambooTracker/gui/command/pattern/set_key_on_to_step_qt_command.hpp --- bambootracker-0.4.5+git20121209/BambooTracker/gui/command/pattern/set_key_on_to_step_qt_command.hpp 2020-12-08 14:05:28.000000000 +0000 +++ bambootracker-0.4.6/BambooTracker/gui/command/pattern/set_key_on_to_step_qt_command.hpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,44 +0,0 @@ -/* - * Copyright (C) 2018 Rerrah - * - * Permission is hereby granted, free of charge, to any person - * obtaining a copy of this software and associated documentation - * files (the "Software"), to deal in the Software without - * restriction, including without limitation the rights to use, - * copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following - * conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES - * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT - * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, - * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR - * OTHER DEALINGS IN THE SOFTWARE. - */ - -#ifndef SET_KEY_ON_TO_STEP_QT_COMMAND_HPP -#define SET_KEY_ON_TO_STEP_QT_COMMAND_HPP - -#include -#include "gui/pattern_editor/pattern_editor_panel.hpp" - -class SetKeyOnToStepQtCommand : public QUndoCommand -{ -public: - SetKeyOnToStepQtCommand(PatternEditorPanel* panel, QUndoCommand* parent = nullptr); - void redo() Q_DECL_OVERRIDE; - void undo() Q_DECL_OVERRIDE; - int id() const Q_DECL_OVERRIDE; - -private: - PatternEditorPanel* panel_; -}; - -#endif // SET_KEY_ON_TO_STEP_QT_COMMAND_HPP diff -Nru bambootracker-0.4.5+git20121209/BambooTracker/gui/command/pattern/set_volume_to_step_qt_command.cpp bambootracker-0.4.6/BambooTracker/gui/command/pattern/set_volume_to_step_qt_command.cpp --- bambootracker-0.4.5+git20121209/BambooTracker/gui/command/pattern/set_volume_to_step_qt_command.cpp 2020-12-08 14:05:28.000000000 +0000 +++ bambootracker-0.4.6/BambooTracker/gui/command/pattern/set_volume_to_step_qt_command.cpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,66 +0,0 @@ -/* - * Copyright (C) 2018-2020 Rerrah - * - * Permission is hereby granted, free of charge, to any person - * obtaining a copy of this software and associated documentation - * files (the "Software"), to deal in the Software without - * restriction, including without limitation the rights to use, - * copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following - * conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES - * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT - * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, - * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR - * OTHER DEALINGS IN THE SOFTWARE. - */ - -#include "set_volume_to_step_qt_command.hpp" -#include "command/command_id.hpp" - -SetVolumeToStepQtCommand::SetVolumeToStepQtCommand(PatternEditorPanel* panel, PatternPosition pos, bool secondEntry, QUndoCommand* parent) - : QUndoCommand(parent), - panel_(panel), - pos_(pos), - isSecondEntry_(secondEntry) -{ -} - -void SetVolumeToStepQtCommand::redo() -{ - panel_->redrawByPatternChanged(); -} - -void SetVolumeToStepQtCommand::undo() -{ - panel_->redrawByPatternChanged(); - panel_->resetEntryCount(); -} - -int SetVolumeToStepQtCommand::id() const -{ - return CommandId::SetVolumeToStep; -} - -bool SetVolumeToStepQtCommand::mergeWith(const QUndoCommand* other) -{ - if (other->id() == id() && !isSecondEntry_) { - auto com = dynamic_cast(other); - if (com->pos_ == pos_ && com->isSecondEntry_) { - isSecondEntry_ = true; - redo(); - return true; - } - } - - isSecondEntry_ = true; - return false; -} diff -Nru bambootracker-0.4.5+git20121209/BambooTracker/gui/command/pattern/set_volume_to_step_qt_command.hpp bambootracker-0.4.6/BambooTracker/gui/command/pattern/set_volume_to_step_qt_command.hpp --- bambootracker-0.4.5+git20121209/BambooTracker/gui/command/pattern/set_volume_to_step_qt_command.hpp 2020-12-08 14:05:28.000000000 +0000 +++ bambootracker-0.4.6/BambooTracker/gui/command/pattern/set_volume_to_step_qt_command.hpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,48 +0,0 @@ -/* - * Copyright (C) 2018-2020 Rerrah - * - * Permission is hereby granted, free of charge, to any person - * obtaining a copy of this software and associated documentation - * files (the "Software"), to deal in the Software without - * restriction, including without limitation the rights to use, - * copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following - * conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES - * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT - * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, - * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR - * OTHER DEALINGS IN THE SOFTWARE. - */ - -#ifndef SET_VOLUME_TO_STEP_QT_COMMAND_HPP -#define SET_VOLUME_TO_STEP_QT_COMMAND_HPP - -#include -#include "gui/pattern_editor/pattern_editor_panel.hpp" -#include "gui/pattern_editor/pattern_position.hpp" - -class SetVolumeToStepQtCommand : public QUndoCommand -{ -public: - SetVolumeToStepQtCommand(PatternEditorPanel* panel, PatternPosition pos, bool secondEntry, QUndoCommand* parent = nullptr); - void redo() Q_DECL_OVERRIDE; - void undo() Q_DECL_OVERRIDE; - int id() const Q_DECL_OVERRIDE; - bool mergeWith(const QUndoCommand* other) Q_DECL_OVERRIDE; - -private: - PatternEditorPanel* panel_; - const PatternPosition pos_; - bool isSecondEntry_; -}; - -#endif // SET_VOLUME_TO_STEP_QT_COMMAND_HPP diff -Nru bambootracker-0.4.5+git20121209/BambooTracker/gui/command/pattern/shrink_pattern_qt_command.cpp bambootracker-0.4.6/BambooTracker/gui/command/pattern/shrink_pattern_qt_command.cpp --- bambootracker-0.4.5+git20121209/BambooTracker/gui/command/pattern/shrink_pattern_qt_command.cpp 2020-12-08 14:05:28.000000000 +0000 +++ bambootracker-0.4.6/BambooTracker/gui/command/pattern/shrink_pattern_qt_command.cpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,48 +0,0 @@ -/* - * Copyright (C) 2018-2019 Rerrah - * - * Permission is hereby granted, free of charge, to any person - * obtaining a copy of this software and associated documentation - * files (the "Software"), to deal in the Software without - * restriction, including without limitation the rights to use, - * copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following - * conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES - * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT - * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, - * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR - * OTHER DEALINGS IN THE SOFTWARE. - */ - -#include "shrink_pattern_qt_command.hpp" -#include "command/command_id.hpp" - -ShrinkPatternQtCommand::ShrinkPatternQtCommand(PatternEditorPanel* panel, QUndoCommand* parent) - : QUndoCommand(parent), - panel_(panel) -{ -} - -void ShrinkPatternQtCommand::redo() -{ - panel_->redrawByPatternChanged(true); -} - -void ShrinkPatternQtCommand::undo() -{ - panel_->redrawByPatternChanged(true); -} - -int ShrinkPatternQtCommand::id() const -{ - return CommandId::ShrinkPattern; -} diff -Nru bambootracker-0.4.5+git20121209/BambooTracker/gui/command/pattern/shrink_pattern_qt_command.hpp bambootracker-0.4.6/BambooTracker/gui/command/pattern/shrink_pattern_qt_command.hpp --- bambootracker-0.4.5+git20121209/BambooTracker/gui/command/pattern/shrink_pattern_qt_command.hpp 2020-12-08 14:05:28.000000000 +0000 +++ bambootracker-0.4.6/BambooTracker/gui/command/pattern/shrink_pattern_qt_command.hpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,44 +0,0 @@ -/* - * Copyright (C) 2018 Rerrah - * - * Permission is hereby granted, free of charge, to any person - * obtaining a copy of this software and associated documentation - * files (the "Software"), to deal in the Software without - * restriction, including without limitation the rights to use, - * copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following - * conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES - * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT - * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, - * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR - * OTHER DEALINGS IN THE SOFTWARE. - */ - -#ifndef SHRINK_PATTERN_QT_COMMAND_HPP -#define SHRINK_PATTERN_QT_COMMAND_HPP - -#include -#include "gui/pattern_editor/pattern_editor_panel.hpp" - -class ShrinkPatternQtCommand : public QUndoCommand -{ -public: - ShrinkPatternQtCommand(PatternEditorPanel* panel, QUndoCommand* parent = nullptr); - void redo() Q_DECL_OVERRIDE; - void undo() Q_DECL_OVERRIDE; - int id() const Q_DECL_OVERRIDE; - -private: - PatternEditorPanel* panel_; -}; - -#endif // SHRINK_PATTERN_QT_COMMAND_HPP diff -Nru bambootracker-0.4.5+git20121209/BambooTracker/gui/command/pattern/transpose_note_in_pattern_qt_command.cpp bambootracker-0.4.6/BambooTracker/gui/command/pattern/transpose_note_in_pattern_qt_command.cpp --- bambootracker-0.4.5+git20121209/BambooTracker/gui/command/pattern/transpose_note_in_pattern_qt_command.cpp 2020-12-08 14:05:28.000000000 +0000 +++ bambootracker-0.4.6/BambooTracker/gui/command/pattern/transpose_note_in_pattern_qt_command.cpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,48 +0,0 @@ -/* - * Copyright (C) 2020 Rerrah - * - * Permission is hereby granted, free of charge, to any person - * obtaining a copy of this software and associated documentation - * files (the "Software"), to deal in the Software without - * restriction, including without limitation the rights to use, - * copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following - * conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES - * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT - * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, - * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR - * OTHER DEALINGS IN THE SOFTWARE. - */ - -#include "transpose_note_in_pattern_qt_command.hpp" -#include "command/command_id.hpp" - -TransposeNoteInPatternQtCommand::TransposeNoteInPatternQtCommand(PatternEditorPanel* panel, QUndoCommand* parent) - : QUndoCommand(parent), - panel_(panel) -{ -} - -void TransposeNoteInPatternQtCommand::redo() -{ - panel_->redrawByPatternChanged(); -} - -void TransposeNoteInPatternQtCommand::undo() -{ - panel_->redrawByPatternChanged(); -} - -int TransposeNoteInPatternQtCommand::id() const -{ - return CommandId::TransposeNoteInPattern; -} diff -Nru bambootracker-0.4.5+git20121209/BambooTracker/gui/command/pattern/transpose_note_in_pattern_qt_command.hpp bambootracker-0.4.6/BambooTracker/gui/command/pattern/transpose_note_in_pattern_qt_command.hpp --- bambootracker-0.4.5+git20121209/BambooTracker/gui/command/pattern/transpose_note_in_pattern_qt_command.hpp 2020-12-08 14:05:28.000000000 +0000 +++ bambootracker-0.4.6/BambooTracker/gui/command/pattern/transpose_note_in_pattern_qt_command.hpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,44 +0,0 @@ -/* - * Copyright (C) 2020 Rerrah - * - * Permission is hereby granted, free of charge, to any person - * obtaining a copy of this software and associated documentation - * files (the "Software"), to deal in the Software without - * restriction, including without limitation the rights to use, - * copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following - * conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES - * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT - * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, - * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR - * OTHER DEALINGS IN THE SOFTWARE. - */ - -#ifndef INCREASE_NOTE_KEY_IN_PATTERN_QT_COMMAND_HPP -#define INCREASE_NOTE_KEY_IN_PATTERN_QT_COMMAND_HPP - -#include -#include "gui/pattern_editor/pattern_editor_panel.hpp" - -class TransposeNoteInPatternQtCommand : public QUndoCommand -{ -public: - TransposeNoteInPatternQtCommand(PatternEditorPanel* panel, QUndoCommand* parent = nullptr); - void redo() Q_DECL_OVERRIDE; - void undo() Q_DECL_OVERRIDE; - int id() const Q_DECL_OVERRIDE; - -private: - PatternEditorPanel* panel_; -}; - -#endif // INCREASE_NOTE_KEY_IN_PATTERN_QT_COMMAND_HPP diff -Nru bambootracker-0.4.5+git20121209/BambooTracker/gui/configuration_dialog.cpp bambootracker-0.4.6/BambooTracker/gui/configuration_dialog.cpp --- bambootracker-0.4.5+git20121209/BambooTracker/gui/configuration_dialog.cpp 2020-12-08 14:05:28.000000000 +0000 +++ bambootracker-0.4.6/BambooTracker/gui/configuration_dialog.cpp 2021-02-11 14:37:17.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (C) 2018-2020 Rerrah + * Copyright (C) 2018-2021 Rerrah * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation @@ -49,6 +49,19 @@ #include "color_palette_handler.hpp" #include "gui_utils.hpp" +namespace +{ +inline Qt::CheckState toCheckState(bool enabled) +{ + return enabled ? Qt::Checked : Qt::Unchecked; +} + +inline bool fromCheckState(Qt::CheckState state) +{ + return (state == Qt::Checked) ? true : false; +} +} + ConfigurationDialog::ConfigurationDialog(std::weak_ptr config, std::weak_ptr palette, std::weak_ptr stream, QWidget *parent) : QDialog(parent), @@ -125,75 +138,75 @@ ui->shortcutsTreeWidget->header()->setSectionResizeMode(0, QHeaderView::Stretch); ui->shortcutsTreeWidget->header()->setSectionResizeMode(1, QHeaderView::Fixed); std::map shortcutsActions = { - { Configuration::KeyOff, tr("Key off") }, - { Configuration::OctaveUp, tr("Octave up") }, - { Configuration::OctaveDown, tr("Octave down") }, - { Configuration::EchoBuffer, tr("Echo buffer") }, - { Configuration::PlayAndStop, tr("Play and stop") }, - { Configuration::Play, tr("Play") }, - { Configuration::PlayFromStart, tr("Play from start") }, - { Configuration::PlayPattern, tr("Play pattern") }, - { Configuration::PlayFromCursor, tr("Play from cursor") }, - { Configuration::PlayFromMarker, tr("Play from marker") }, - { Configuration::PlayStep, tr("Play step") }, - { Configuration::Stop, tr("Stop") }, - { Configuration::FocusOnPattern, tr("Focus on pattern editor") }, - { Configuration::FocusOnOrder, tr("Focus on order list") }, - { Configuration::FocusOnInstrument, tr("Focus on instrument list") }, - { Configuration::ToggleEditJam, tr("Toggle edit/jam mode") }, - { Configuration::SetMarker, tr("Set marker") }, - { Configuration::PasteMix, tr("Paste and mix") }, - { Configuration::PasteOverwrite, tr("Paste and overwrite") }, - { Configuration::PasteInsert, tr("Paste and insert") }, - { Configuration::SelectAll, tr("Select all") }, - { Configuration::Deselect, tr("Deselect") }, - { Configuration::SelectRow, tr("Select row") }, - { Configuration::SelectColumn, tr("Select column") }, - { Configuration::SelectPattern, tr("Select pattern") }, - { Configuration::SelectOrder, tr("Select order") }, - { Configuration::GoToStep, tr("Go to step") }, - { Configuration::ToggleTrack, tr("Toggle track") }, - { Configuration::SoloTrack, tr("Solo track") }, - { Configuration::Interpolate, tr("Interpolate") }, - { Configuration::Reverse, tr("Reverse") }, - { Configuration::GoToPrevOrder, tr("Go to previous order") }, - { Configuration::GoToNextOrder, tr("Go to next order") }, - { Configuration::ToggleBookmark, tr("Toggle bookmark") }, - { Configuration::PrevBookmark, tr("Previous bookmark") }, - { Configuration::NextBookmark, tr("Next bookmark") }, - { Configuration::DecreaseNote, tr("Transpose, decrease note") }, - { Configuration::IncreaseNote, tr("Transpose, increase note") }, - { Configuration::DecreaseOctave, tr("Transpose, decrease octave") }, - { Configuration::IncreaseOctave, tr("Transpose, increase octave") }, - { Configuration::PrevInstrument, tr("Previous instrument") }, - { Configuration::NextInstrument, tr("Next instrument") }, - { Configuration::MaskInstrument, tr("Mask instrument") }, - { Configuration::MaskVolume, tr("Mask volume") }, - { Configuration::EditInstrument, tr("Edit instrument") }, - { Configuration::FollowMode, tr("Follow mode") }, - { Configuration::DuplicateOrder, tr("Duplicate order") }, - { Configuration::ClonePatterns, tr("Clone patterns") }, - { Configuration::CloneOrder, tr("Clone order") }, - { Configuration::ReplaceInstrument, tr("Replace instrument") }, - { Configuration::ExpandPattern, tr("Expand pattern") }, - { Configuration::ShrinkPattern, tr("Shrink pattern") }, - { Configuration::FineDecreaseValues, tr("Fine decrease values") }, - { Configuration::FineIncreaseValues, tr("Fine increase values") }, - { Configuration::CoarseDecreaseValues, tr("Coarse decrease values") }, - { Configuration::CoarseIncreaseValuse, tr("Coarse increase valuse") }, - { Configuration::ExpandEffect, tr("Expand effect column") }, - { Configuration::ShrinkEffect, tr("Shrink effect column") }, - { Configuration::PrevHighlighted, tr("Previous highlighted step") }, - { Configuration::NextHighlighted, tr("Next highlighted step") }, - { Configuration::IncreasePatternSize, tr("Increase pattern size") }, - { Configuration::DecreasePatternSize, tr("Decrease pattern size") }, - { Configuration::IncreaseEditStep, tr("Increase edit step") }, - { Configuration::DecreaseEditStep, tr("Decrease edit step") }, - { Configuration::DisplayEffectList, tr("Display effect list") }, - { Configuration::PreviousSong, tr("Previous song") }, - { Configuration::NextSong, tr("Next song") }, - { Configuration::JamVolumeUp, tr("Jam volume up") }, - { Configuration::JamVolumeDown, tr("Jam volume down") } + { Configuration::ShortcutAction::KeyOff, tr("Key off") }, + { Configuration::ShortcutAction::OctaveUp, tr("Octave up") }, + { Configuration::ShortcutAction::OctaveDown, tr("Octave down") }, + { Configuration::ShortcutAction::EchoBuffer, tr("Echo buffer") }, + { Configuration::ShortcutAction::PlayAndStop, tr("Play and stop") }, + { Configuration::ShortcutAction::Play, tr("Play") }, + { Configuration::ShortcutAction::PlayFromStart, tr("Play from start") }, + { Configuration::ShortcutAction::PlayPattern, tr("Play pattern") }, + { Configuration::ShortcutAction::PlayFromCursor, tr("Play from cursor") }, + { Configuration::ShortcutAction::PlayFromMarker, tr("Play from marker") }, + { Configuration::ShortcutAction::PlayStep, tr("Play step") }, + { Configuration::ShortcutAction::Stop, tr("Stop") }, + { Configuration::ShortcutAction::FocusOnPattern, tr("Focus on pattern editor") }, + { Configuration::ShortcutAction::FocusOnOrder, tr("Focus on order list") }, + { Configuration::ShortcutAction::FocusOnInstrument, tr("Focus on instrument list") }, + { Configuration::ShortcutAction::ToggleEditJam, tr("Toggle edit/jam mode") }, + { Configuration::ShortcutAction::SetMarker, tr("Set marker") }, + { Configuration::ShortcutAction::PasteMix, tr("Paste and mix") }, + { Configuration::ShortcutAction::PasteOverwrite, tr("Paste and overwrite") }, + { Configuration::ShortcutAction::PasteInsert, tr("Paste and insert") }, + { Configuration::ShortcutAction::SelectAll, tr("Select all") }, + { Configuration::ShortcutAction::Deselect, tr("Deselect") }, + { Configuration::ShortcutAction::SelectRow, tr("Select row") }, + { Configuration::ShortcutAction::SelectColumn, tr("Select column") }, + { Configuration::ShortcutAction::SelectPattern, tr("Select pattern") }, + { Configuration::ShortcutAction::SelectOrder, tr("Select order") }, + { Configuration::ShortcutAction::GoToStep, tr("Go to step") }, + { Configuration::ShortcutAction::ToggleTrack, tr("Toggle track") }, + { Configuration::ShortcutAction::SoloTrack, tr("Solo track") }, + { Configuration::ShortcutAction::Interpolate, tr("Interpolate") }, + { Configuration::ShortcutAction::Reverse, tr("Reverse") }, + { Configuration::ShortcutAction::GoToPrevOrder, tr("Go to previous order") }, + { Configuration::ShortcutAction::GoToNextOrder, tr("Go to next order") }, + { Configuration::ShortcutAction::ToggleBookmark, tr("Toggle bookmark") }, + { Configuration::ShortcutAction::PrevBookmark, tr("Previous bookmark") }, + { Configuration::ShortcutAction::NextBookmark, tr("Next bookmark") }, + { Configuration::ShortcutAction::DecreaseNote, tr("Transpose, decrease note") }, + { Configuration::ShortcutAction::IncreaseNote, tr("Transpose, increase note") }, + { Configuration::ShortcutAction::DecreaseOctave, tr("Transpose, decrease octave") }, + { Configuration::ShortcutAction::IncreaseOctave, tr("Transpose, increase octave") }, + { Configuration::ShortcutAction::PrevInstrument, tr("Previous instrument") }, + { Configuration::ShortcutAction::NextInstrument, tr("Next instrument") }, + { Configuration::ShortcutAction::MaskInstrument, tr("Mask instrument") }, + { Configuration::ShortcutAction::MaskVolume, tr("Mask volume") }, + { Configuration::ShortcutAction::EditInstrument, tr("Edit instrument") }, + { Configuration::ShortcutAction::FollowMode, tr("Follow mode") }, + { Configuration::ShortcutAction::DuplicateOrder, tr("Duplicate order") }, + { Configuration::ShortcutAction::ClonePatterns, tr("Clone patterns") }, + { Configuration::ShortcutAction::CloneOrder, tr("Clone order") }, + { Configuration::ShortcutAction::ReplaceInstrument, tr("Replace instrument") }, + { Configuration::ShortcutAction::ExpandPattern, tr("Expand pattern") }, + { Configuration::ShortcutAction::ShrinkPattern, tr("Shrink pattern") }, + { Configuration::ShortcutAction::FineDecreaseValues, tr("Fine decrease values") }, + { Configuration::ShortcutAction::FineIncreaseValues, tr("Fine increase values") }, + { Configuration::ShortcutAction::CoarseDecreaseValues, tr("Coarse decrease values") }, + { Configuration::ShortcutAction::CoarseIncreaseValuse, tr("Coarse increase valuse") }, + { Configuration::ShortcutAction::ExpandEffect, tr("Expand effect column") }, + { Configuration::ShortcutAction::ShrinkEffect, tr("Shrink effect column") }, + { Configuration::ShortcutAction::PrevHighlighted, tr("Previous highlighted step") }, + { Configuration::ShortcutAction::NextHighlighted, tr("Next highlighted step") }, + { Configuration::ShortcutAction::IncreasePatternSize, tr("Increase pattern size") }, + { Configuration::ShortcutAction::DecreasePatternSize, tr("Decrease pattern size") }, + { Configuration::ShortcutAction::IncreaseEditStep, tr("Increase edit step") }, + { Configuration::ShortcutAction::DecreaseEditStep, tr("Decrease edit step") }, + { Configuration::ShortcutAction::DisplayEffectList, tr("Display effect list") }, + { Configuration::ShortcutAction::PreviousSong, tr("Previous song") }, + { Configuration::ShortcutAction::NextSong, tr("Next song") }, + { Configuration::ShortcutAction::JamVolumeUp, tr("Jam volume up") }, + { Configuration::ShortcutAction::JamVolumeDown, tr("Jam volume down") } }; std::unordered_map shortcuts = configLocked->getShortcuts(); for (const auto& pair : shortcutsActions) { diff -Nru bambootracker-0.4.5+git20121209/BambooTracker/gui/configuration_dialog.hpp bambootracker-0.4.6/BambooTracker/gui/configuration_dialog.hpp --- bambootracker-0.4.5+git20121209/BambooTracker/gui/configuration_dialog.hpp 2020-12-08 14:05:28.000000000 +0000 +++ bambootracker-0.4.6/BambooTracker/gui/configuration_dialog.hpp 2021-02-11 14:37:17.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (C) 2018-2020 Rerrah + * Copyright (C) 2018-2021 Rerrah * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation @@ -36,7 +36,6 @@ #include "configuration.hpp" #include "color_palette.hpp" #include "enum_hash.hpp" -#include "misc.hpp" namespace Ui { class ConfigurationDialog; @@ -65,16 +64,6 @@ std::weak_ptr refPalette_; std::weak_ptr stream_; - inline Qt::CheckState toCheckState(bool enabled) - { - return enabled ? Qt::Checked : Qt::Unchecked; - } - - inline bool fromCheckState(Qt::CheckState state) - { - return (state == Qt::Checked) ? true : false; - } - std::unordered_map customLayoutKeysMap_; /***** General *****/ diff -Nru bambootracker-0.4.5+git20121209/BambooTracker/gui/configuration_handler.cpp bambootracker-0.4.6/BambooTracker/gui/configuration_handler.cpp --- bambootracker-0.4.5+git20121209/BambooTracker/gui/configuration_handler.cpp 2020-12-08 14:05:28.000000000 +0000 +++ bambootracker-0.4.6/BambooTracker/gui/configuration_handler.cpp 2021-02-11 14:37:17.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (C) 2018-2020 Rerrah + * Copyright (C) 2018-2021 Rerrah * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation @@ -26,86 +26,95 @@ #include "configuration_handler.hpp" #include #include +#include +#include +#include "configuration.hpp" #include "jamming.hpp" -#include"enum_hash.hpp" +#include "enum_hash.hpp" +#include "gui/gui_utils.hpp" +#include "utils.hpp" +namespace io +{ +namespace +{ // config path (*nix): ~/.config//.ini -const QString ConfigurationHandler::ORGANIZATION_ = "BambooTracker"; -const QString ConfigurationHandler::APPLICATION_ = "BambooTracker"; +const QString ORG_NAME = "BambooTracker"; +const QString APP_NAME = "BambooTracker"; -const std::unordered_map ConfigurationHandler::SHORTCUTS_NAME_MAP_ = { - { Configuration::KeyOff, "keyOff" }, - { Configuration::OctaveUp, "octaveUp" }, - { Configuration::OctaveDown, "octaveDown" }, - { Configuration::EchoBuffer, "echoBuffer" }, - { Configuration::PlayAndStop, "playAndStop" }, - { Configuration::Play, "play" }, - { Configuration::PlayFromStart, "playFromStart" }, - { Configuration::PlayPattern, "playPattern" }, - { Configuration::PlayFromCursor, "playFromCursor" }, - { Configuration::PlayFromMarker, "playFromMarker" }, - { Configuration::PlayStep, "playStep" }, - { Configuration::Stop, "stop" }, - { Configuration::FocusOnPattern, "ocusOnPattern" }, - { Configuration::FocusOnOrder, "focusOnOrder" }, - { Configuration::FocusOnInstrument, "focusOnInstrument" }, - { Configuration::ToggleEditJam, "toggleEditJam" }, - { Configuration::SetMarker, "setMarker" }, - { Configuration::PasteMix, "pasteMix" }, - { Configuration::PasteOverwrite, "pasteOverwrite" }, - { Configuration::PasteInsert, "pasteInsert" }, - { Configuration::SelectAll, "selectAll" }, - { Configuration::Deselect, "deselect" }, - { Configuration::SelectRow, "selectRow" }, - { Configuration::SelectColumn, "selectColumn" }, - { Configuration::SelectPattern, "selectPattern" }, - { Configuration::SelectOrder, "selectOrder" }, - { Configuration::GoToStep, "goToStep" }, - { Configuration::ToggleTrack, "toggleTrack" }, - { Configuration::SoloTrack, "soloTrack" }, - { Configuration::Interpolate, "interpolate" }, - { Configuration::Reverse, "reverse" }, - { Configuration::GoToPrevOrder, "goToPrevOrder" }, - { Configuration::GoToNextOrder, "goToNextOrder" }, - { Configuration::ToggleBookmark, "toggleBookmark" }, - { Configuration::PrevBookmark, "prevBookmark" }, - { Configuration::NextBookmark, "nextBookmark" }, - { Configuration::DecreaseNote, "decreaseNote" }, - { Configuration::IncreaseNote, "increaseNote" }, - { Configuration::DecreaseOctave, "decreaseOctave" }, - { Configuration::IncreaseOctave, "increaseOctave" }, - { Configuration::PrevInstrument, "prevInstrument" }, - { Configuration::NextInstrument, "nextInstrument" }, - { Configuration::MaskInstrument, "maskInstrument" }, - { Configuration::MaskVolume, "maskVolume" }, - { Configuration::EditInstrument, "editInstrument" }, - { Configuration::FollowMode, "followMode" }, - { Configuration::DuplicateOrder, "duplicateOrder" }, - { Configuration::ClonePatterns, "clonePatterns" }, - { Configuration::CloneOrder, "cloneOrder" }, - { Configuration::ReplaceInstrument, "replaceInstrument" }, - { Configuration::ExpandPattern, "expandPattern" }, - { Configuration::ShrinkPattern, "shrinkPattern" }, - { Configuration::FineDecreaseValues, "fineDecreaseValues" }, - { Configuration::FineIncreaseValues, "fineIncreaseValues" }, - { Configuration::CoarseDecreaseValues, "coarseDecreaseValues" }, - { Configuration::CoarseIncreaseValuse, "coarseIncreaseValuse" }, - { Configuration::ExpandEffect, "expandEffect" }, - { Configuration::ShrinkEffect, "shrinkEffect" }, - { Configuration::PrevHighlighted, "prevHighlightedStep" }, - { Configuration::NextHighlighted, "nextHighlightedStep" }, - { Configuration::IncreasePatternSize, "incPtnSize" }, - { Configuration::DecreasePatternSize, "decPtnSize" }, - { Configuration::IncreaseEditStep, "incEditStep" }, - { Configuration::DecreaseEditStep, "decEditStep" }, - { Configuration::DisplayEffectList, "dispEffectList" }, - { Configuration::PreviousSong, "prevSong" }, - { Configuration::NextSong, "nextSong" }, - { Configuration::JamVolumeUp, "jamVolumeUp" }, - { Configuration::JamVolumeDown, "jamVolumeDown" } +const std::unordered_map SHORTCUTS_NAME_MAP = { + { Configuration::ShortcutAction::KeyOff, "keyOff" }, + { Configuration::ShortcutAction::OctaveUp, "octaveUp" }, + { Configuration::ShortcutAction::OctaveDown, "octaveDown" }, + { Configuration::ShortcutAction::EchoBuffer, "echoBuffer" }, + { Configuration::ShortcutAction::PlayAndStop, "playAndStop" }, + { Configuration::ShortcutAction::Play, "play" }, + { Configuration::ShortcutAction::PlayFromStart, "playFromStart" }, + { Configuration::ShortcutAction::PlayPattern, "playPattern" }, + { Configuration::ShortcutAction::PlayFromCursor, "playFromCursor" }, + { Configuration::ShortcutAction::PlayFromMarker, "playFromMarker" }, + { Configuration::ShortcutAction::PlayStep, "playStep" }, + { Configuration::ShortcutAction::Stop, "stop" }, + { Configuration::ShortcutAction::FocusOnPattern, "ocusOnPattern" }, + { Configuration::ShortcutAction::FocusOnOrder, "focusOnOrder" }, + { Configuration::ShortcutAction::FocusOnInstrument, "focusOnInstrument" }, + { Configuration::ShortcutAction::ToggleEditJam, "toggleEditJam" }, + { Configuration::ShortcutAction::SetMarker, "setMarker" }, + { Configuration::ShortcutAction::PasteMix, "pasteMix" }, + { Configuration::ShortcutAction::PasteOverwrite, "pasteOverwrite" }, + { Configuration::ShortcutAction::PasteInsert, "pasteInsert" }, + { Configuration::ShortcutAction::SelectAll, "selectAll" }, + { Configuration::ShortcutAction::Deselect, "deselect" }, + { Configuration::ShortcutAction::SelectRow, "selectRow" }, + { Configuration::ShortcutAction::SelectColumn, "selectColumn" }, + { Configuration::ShortcutAction::SelectPattern, "selectPattern" }, + { Configuration::ShortcutAction::SelectOrder, "selectOrder" }, + { Configuration::ShortcutAction::GoToStep, "goToStep" }, + { Configuration::ShortcutAction::ToggleTrack, "toggleTrack" }, + { Configuration::ShortcutAction::SoloTrack, "soloTrack" }, + { Configuration::ShortcutAction::Interpolate, "interpolate" }, + { Configuration::ShortcutAction::Reverse, "reverse" }, + { Configuration::ShortcutAction::GoToPrevOrder, "goToPrevOrder" }, + { Configuration::ShortcutAction::GoToNextOrder, "goToNextOrder" }, + { Configuration::ShortcutAction::ToggleBookmark, "toggleBookmark" }, + { Configuration::ShortcutAction::PrevBookmark, "prevBookmark" }, + { Configuration::ShortcutAction::NextBookmark, "nextBookmark" }, + { Configuration::ShortcutAction::DecreaseNote, "decreaseNote" }, + { Configuration::ShortcutAction::IncreaseNote, "increaseNote" }, + { Configuration::ShortcutAction::DecreaseOctave, "decreaseOctave" }, + { Configuration::ShortcutAction::IncreaseOctave, "increaseOctave" }, + { Configuration::ShortcutAction::PrevInstrument, "prevInstrument" }, + { Configuration::ShortcutAction::NextInstrument, "nextInstrument" }, + { Configuration::ShortcutAction::MaskInstrument, "maskInstrument" }, + { Configuration::ShortcutAction::MaskVolume, "maskVolume" }, + { Configuration::ShortcutAction::EditInstrument, "editInstrument" }, + { Configuration::ShortcutAction::FollowMode, "followMode" }, + { Configuration::ShortcutAction::DuplicateOrder, "duplicateOrder" }, + { Configuration::ShortcutAction::ClonePatterns, "clonePatterns" }, + { Configuration::ShortcutAction::CloneOrder, "cloneOrder" }, + { Configuration::ShortcutAction::ReplaceInstrument, "replaceInstrument" }, + { Configuration::ShortcutAction::ExpandPattern, "expandPattern" }, + { Configuration::ShortcutAction::ShrinkPattern, "shrinkPattern" }, + { Configuration::ShortcutAction::FineDecreaseValues, "fineDecreaseValues" }, + { Configuration::ShortcutAction::FineIncreaseValues, "fineIncreaseValues" }, + { Configuration::ShortcutAction::CoarseDecreaseValues, "coarseDecreaseValues" }, + { Configuration::ShortcutAction::CoarseIncreaseValuse, "coarseIncreaseValuse" }, + { Configuration::ShortcutAction::ExpandEffect, "expandEffect" }, + { Configuration::ShortcutAction::ShrinkEffect, "shrinkEffect" }, + { Configuration::ShortcutAction::PrevHighlighted, "prevHighlightedStep" }, + { Configuration::ShortcutAction::NextHighlighted, "nextHighlightedStep" }, + { Configuration::ShortcutAction::IncreasePatternSize, "incPtnSize" }, + { Configuration::ShortcutAction::DecreasePatternSize, "decPtnSize" }, + { Configuration::ShortcutAction::IncreaseEditStep, "incEditStep" }, + { Configuration::ShortcutAction::DecreaseEditStep, "decEditStep" }, + { Configuration::ShortcutAction::DisplayEffectList, "dispEffectList" }, + { Configuration::ShortcutAction::PreviousSong, "prevSong" }, + { Configuration::ShortcutAction::NextSong, "nextSong" }, + { Configuration::ShortcutAction::JamVolumeUp, "jamVolumeUp" }, + { Configuration::ShortcutAction::JamVolumeDown, "jamVolumeDown" } }; -const std::unordered_map ConfigurationHandler::JAM_KEY_NAME_MAP_ = { +const std::unordered_map JAM_KEY_NAME_MAP = { {JamKey::LowC, "lowC"}, {JamKey::LowCS, "lowCS"}, {JamKey::LowD, "lowD"}, @@ -138,13 +147,12 @@ {JamKey::HighCS2, "highHighCS"}, {JamKey::HighD2, "highHighD"} }; +} -ConfigurationHandler::ConfigurationHandler() {} - -bool ConfigurationHandler::saveConfiguration(std::weak_ptr config) +bool saveConfiguration(std::weak_ptr config) { try { - QSettings settings(QSettings::IniFormat, QSettings::UserScope, ConfigurationHandler::ORGANIZATION_, ConfigurationHandler::APPLICATION_); + QSettings settings(QSettings::IniFormat, QSettings::UserScope, ORG_NAME, APP_NAME); std::shared_ptr configLocked = config.lock(); // Internal // @@ -172,15 +180,15 @@ settings.setValue("visibleToolbar", configLocked->getVisibleToolbar()); settings.setValue("visibleStatusBar", configLocked->getVisibleStatusBar()); settings.setValue("visibleWaveView", configLocked->getVisibleWaveView()); - settings.setValue("pasteMode", configLocked->getPasteMode()); + settings.setValue("pasteMode", static_cast(configLocked->getPasteMode())); auto& mainTbConfig = configLocked->getMainToolbarConfiguration(); - settings.setValue("mainToolbarPosition", mainTbConfig.getPosition()); + settings.setValue("mainToolbarPosition", static_cast(mainTbConfig.getPosition())); settings.setValue("mainToolbarNumber", mainTbConfig.getNumber()); settings.setValue("hasBreakBeforeMainToolbar", mainTbConfig.hasBreakBefore()); settings.setValue("mainToolbarX", mainTbConfig.getX()); settings.setValue("mainToolbarY", mainTbConfig.getY()); auto& subTbConfig = configLocked->getSubToolbarConfiguration(); - settings.setValue("subToolbarPosition", subTbConfig.getPosition()); + settings.setValue("subToolbarPosition", static_cast(subTbConfig.getPosition())); settings.setValue("subToolbarNumber", subTbConfig.getNumber()); settings.setValue("hasBreakBeforesubToolbar", subTbConfig.hasBreakBefore()); settings.setValue("subToolbarX", subTbConfig.getX()); @@ -226,11 +234,11 @@ // Keys settings.beginGroup("Keys"); for (const auto& pair : configLocked->getShortcuts()) { - settings.setValue("shortcut_" + SHORTCUTS_NAME_MAP_.at(pair.first), gui_utils::utf8ToQString(pair.second)); + settings.setValue("shortcut_" + SHORTCUTS_NAME_MAP.at(pair.first), gui_utils::utf8ToQString(pair.second)); } settings.setValue("noteEntryLayout", static_cast(configLocked->getNoteEntryLayout())); for (const auto& pair : configLocked->getCustomLayoutKeys()) { - settings.setValue("customLayout_" + JAM_KEY_NAME_MAP_.at(pair.second), gui_utils::utf8ToQString(pair.first)); + settings.setValue("customLayout_" + JAM_KEY_NAME_MAP.at(pair.second), gui_utils::utf8ToQString(pair.first)); } settings.endGroup(); @@ -262,7 +270,7 @@ settings.beginGroup("Input"); settings.beginWriteArray("fmEnvelopeTextMap"); int n = 0; - for (auto texts : config.lock()->getFMEnvelopeTexts()) { + for (const FMEnvelopeText& texts : config.lock()->getFMEnvelopeTexts()) { settings.setArrayIndex(n++); settings.setValue("type", gui_utils::utf8ToQString(texts.name)); QStringList typeList; @@ -290,10 +298,10 @@ } } -bool ConfigurationHandler::loadConfiguration(std::weak_ptr config) +bool loadConfiguration(std::weak_ptr config) { try { - QSettings settings(QSettings::IniFormat, QSettings::UserScope, ConfigurationHandler::ORGANIZATION_, ConfigurationHandler::APPLICATION_); + QSettings settings(QSettings::IniFormat, QSettings::UserScope, ORG_NAME, APP_NAME); std::shared_ptr configLocked = config.lock(); // Internal // @@ -321,15 +329,15 @@ configLocked->setVisibleToolbar(settings.value("visibleToolbar", configLocked->getVisibleToolbar()).toBool()); configLocked->setVisibleStatusBar(settings.value("visibleStatusBar", configLocked->getVisibleStatusBar()).toBool()); configLocked->setVisibleWaveView(settings.value("visibleWaveView", configLocked->getVisibleWaveView()).toBool()); - configLocked->setPasteMode(static_cast(settings.value("pasteMode", configLocked->getPasteMode()).toInt())); + configLocked->setPasteMode(static_cast(settings.value("pasteMode", static_cast(configLocked->getPasteMode())).toInt())); auto& mainTbConfig = configLocked->getMainToolbarConfiguration(); - mainTbConfig.setPosition(static_cast(settings.value("mainToolbarPosition", mainTbConfig.getPosition()).toInt())); + mainTbConfig.setPosition(static_cast(settings.value("mainToolbarPosition", static_cast(mainTbConfig.getPosition())).toInt())); mainTbConfig.setNumber(settings.value("mainToolbarNumber", mainTbConfig.getNumber()).toInt()); mainTbConfig.setBreakBefore(settings.value("hasBreakBeforeMainToolbar", mainTbConfig.hasBreakBefore()).toBool()); mainTbConfig.setX(settings.value("mainToolbarX", mainTbConfig.getX()).toInt()); mainTbConfig.setY(settings.value("mainToolbarY", mainTbConfig.getY()).toInt()); auto& subTbConfig = configLocked->getSubToolbarConfiguration(); - subTbConfig.setPosition(static_cast(settings.value("subToolbarPosition", subTbConfig.getPosition()).toInt())); + subTbConfig.setPosition(static_cast(settings.value("subToolbarPosition", static_cast(subTbConfig.getPosition())).toInt())); subTbConfig.setNumber(settings.value("subToolbarNumber", subTbConfig.getNumber()).toInt()); subTbConfig.setBreakBefore(settings.value("hasBreakBeforesubToolbar", subTbConfig.hasBreakBefore()).toBool()); subTbConfig.setX(settings.value("subToolbarX", subTbConfig.getX()).toInt()); @@ -387,7 +395,7 @@ // Keys settings.beginGroup("Keys"); std::unordered_map shortcuts; - for (const auto& pair : SHORTCUTS_NAME_MAP_) { + for (const auto& pair : SHORTCUTS_NAME_MAP) { std::string def = configLocked->getShortcuts().at(pair.first); shortcuts[pair.first] = settings.value("shortcut_" + pair.second, gui_utils::utf8ToQString(def)).toString().toUtf8().toStdString(); } @@ -396,12 +404,11 @@ settings.value("noteEntryLayout", static_cast(configLocked->getNoteEntryLayout())).toInt())); std::unordered_map customLayoutNewKeys; - for (const auto& pair : JAM_KEY_NAME_MAP_) { + for (const auto& pair : JAM_KEY_NAME_MAP) { JamKey currentlyWantedJamKey = pair.first; customLayoutNewKeys[ settings.value("customLayout_" + pair.second, - QString::fromStdString((*std::find_if (configLocked->mappingLayouts.at(Configuration::QWERTY).begin(), - configLocked->mappingLayouts.at(Configuration::QWERTY).end(), + QString::fromStdString((*utils::findIf(configLocked->mappingLayouts.at(Configuration::KeyboardLayout::QWERTY), [currentlyWantedJamKey](const std::pair& t) -> bool { return (t.second) == currentlyWantedJamKey;}) ).first)).toString().toUtf8().toStdString()] @@ -447,7 +454,8 @@ settings.setArrayIndex(i); std::string type = settings.value("type").toString().toUtf8().toStdString(); std::vector data; - for (auto d : settings.value("order").toString().split(",")) { + const QStringList list = settings.value("order").toString().split(","); + for (const QString& d : list) { data.push_back(static_cast(d.toInt())); } fmEnvelopeTexts.push_back({ type, data }); @@ -473,3 +481,4 @@ return false; } } +} diff -Nru bambootracker-0.4.5+git20121209/BambooTracker/gui/configuration_handler.hpp bambootracker-0.4.6/BambooTracker/gui/configuration_handler.hpp --- bambootracker-0.4.5+git20121209/BambooTracker/gui/configuration_handler.hpp 2020-12-08 14:05:28.000000000 +0000 +++ bambootracker-0.4.6/BambooTracker/gui/configuration_handler.hpp 2021-02-11 14:37:17.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (C) 2018-2020 Rerrah + * Copyright (C) 2018-2021 Rerrah * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation @@ -23,36 +23,17 @@ * OTHER DEALINGS IN THE SOFTWARE. */ -#ifndef CONF_HPP -#define CONF_HPP +#ifndef CONFIGURATION_HANDLER_HPP +#define CONFIGURATION_HANDLER_HPP #include -#include -#include -#include -#include "configuration.hpp" -#include "gui/gui_utils.hpp" -enum class JamKey : int; +class Configuration; -class ConfigurationHandler -{ -public: - static bool saveConfiguration(std::weak_ptr config); - static bool loadConfiguration(std::weak_ptr config); +namespace io +{ +bool saveConfiguration(std::weak_ptr config); +bool loadConfiguration(std::weak_ptr config); +} -private: - ConfigurationHandler(); - const static QString ORGANIZATION_; - const static QString APPLICATION_; - - const static std::unordered_map SHORTCUTS_NAME_MAP_; - const static std::unordered_map JAM_KEY_NAME_MAP_; - - static inline std::string loadShortcut(const QSettings& settings, const QString key, const std::string shortcut) - { - return settings.value(key, gui_utils::utf8ToQString(shortcut)).toString().toUtf8().toStdString(); - } -}; - -#endif // CONF_HPP +#endif // CONFIGURATION_HANDLER_HPP diff -Nru bambootracker-0.4.5+git20121209/BambooTracker/gui/effect_list_dialog.cpp bambootracker-0.4.6/BambooTracker/gui/effect_list_dialog.cpp --- bambootracker-0.4.5+git20121209/BambooTracker/gui/effect_list_dialog.cpp 2020-12-08 14:05:28.000000000 +0000 +++ bambootracker-0.4.6/BambooTracker/gui/effect_list_dialog.cpp 2021-02-11 14:37:17.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (C) 2019-2020 Rerrah + * Copyright (C) 2019-2021 Rerrah * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation @@ -26,6 +26,7 @@ #include "effect_list_dialog.hpp" #include "ui_effect_list_dialog.h" #include +#include #include "gui/effect_description.hpp" EffectListDialog::EffectListDialog(QWidget *parent) : diff -Nru bambootracker-0.4.5+git20121209/BambooTracker/gui/effect_list_dialog.hpp bambootracker-0.4.6/BambooTracker/gui/effect_list_dialog.hpp --- bambootracker-0.4.5+git20121209/BambooTracker/gui/effect_list_dialog.hpp 2020-12-08 14:05:28.000000000 +0000 +++ bambootracker-0.4.6/BambooTracker/gui/effect_list_dialog.hpp 2021-02-11 14:37:17.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (C) 2019-2020 Rerrah + * Copyright (C) 2019-2021 Rerrah * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation @@ -27,11 +27,9 @@ #define EFFECT_LIST_DIALOG_HPP #include -#include #include #include "effect.hpp" #include "enum_hash.hpp" -#include "misc.hpp" namespace Ui { class EffectListDialog; diff -Nru bambootracker-0.4.5+git20121209/BambooTracker/gui/file_history.cpp bambootracker-0.4.6/BambooTracker/gui/file_history.cpp --- bambootracker-0.4.5+git20121209/BambooTracker/gui/file_history.cpp 2020-12-08 14:05:28.000000000 +0000 +++ bambootracker-0.4.6/BambooTracker/gui/file_history.cpp 2021-02-11 14:37:17.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (C) 2019 Rerrah + * Copyright (C) 2019-2021 Rerrah * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation @@ -25,17 +25,21 @@ #include "file_history.hpp" #include +#include "utils.hpp" -const int FileHistory::HISTORY_SIZE_ = 8; +namespace +{ +const int HISTORY_SIZE = 8; +} FileHistory::FileHistory() {} void FileHistory::addFile(QString path) { - auto it = std::find_if(list_.begin(), list_.end(), [&path](QString& p) { return (path == p); }); + auto it = utils::find(list_, path); if (it != list_.end()) list_.erase(it); list_.push_front(path); - if (list_.size() > HISTORY_SIZE_) list_.pop_back(); + if (list_.size() > HISTORY_SIZE) list_.pop_back(); } void FileHistory::clearHistory() diff -Nru bambootracker-0.4.5+git20121209/BambooTracker/gui/file_history.hpp bambootracker-0.4.6/BambooTracker/gui/file_history.hpp --- bambootracker-0.4.5+git20121209/BambooTracker/gui/file_history.hpp 2020-12-08 14:05:28.000000000 +0000 +++ bambootracker-0.4.6/BambooTracker/gui/file_history.hpp 2021-02-11 14:37:17.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (C) 2019 Rerrah + * Copyright (C) 2019-2021 Rerrah * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation @@ -41,7 +41,6 @@ bool empty() const; private: - static const int HISTORY_SIZE_; std::deque list_; }; diff -Nru bambootracker-0.4.5+git20121209/BambooTracker/gui/fm_envelope_set_edit_dialog.cpp bambootracker-0.4.6/BambooTracker/gui/fm_envelope_set_edit_dialog.cpp --- bambootracker-0.4.5+git20121209/BambooTracker/gui/fm_envelope_set_edit_dialog.cpp 2020-12-08 14:05:28.000000000 +0000 +++ bambootracker-0.4.6/BambooTracker/gui/fm_envelope_set_edit_dialog.cpp 2021-02-11 14:37:17.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (C) 2019-2020 Rerrah + * Copyright (C) 2019-2021 Rerrah * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation @@ -26,6 +26,7 @@ #include "fm_envelope_set_edit_dialog.hpp" #include "ui_fm_envelope_set_edit_dialog.h" #include +#include "configuration.hpp" FMEnvelopeSetEditDialog::FMEnvelopeSetEditDialog(std::vector set, QWidget *parent) : QDialog(parent), diff -Nru bambootracker-0.4.5+git20121209/BambooTracker/gui/fm_envelope_set_edit_dialog.hpp bambootracker-0.4.6/BambooTracker/gui/fm_envelope_set_edit_dialog.hpp --- bambootracker-0.4.5+git20121209/BambooTracker/gui/fm_envelope_set_edit_dialog.hpp 2020-12-08 14:05:28.000000000 +0000 +++ bambootracker-0.4.6/BambooTracker/gui/fm_envelope_set_edit_dialog.hpp 2021-02-11 14:37:17.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (C) 2019 Rerrah + * Copyright (C) 2019-2021 Rerrah * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation @@ -29,12 +29,13 @@ #include #include #include -#include "misc.hpp" namespace Ui { class FMEnvelopeSetEditDialog; } +enum class FMEnvelopeTextType; + class FMEnvelopeSetEditDialog : public QDialog { Q_OBJECT diff -Nru bambootracker-0.4.5+git20121209/BambooTracker/gui/groove_settings_dialog.cpp bambootracker-0.4.6/BambooTracker/gui/groove_settings_dialog.cpp --- bambootracker-0.4.5+git20121209/BambooTracker/gui/groove_settings_dialog.cpp 2020-12-08 14:05:28.000000000 +0000 +++ bambootracker-0.4.6/BambooTracker/gui/groove_settings_dialog.cpp 2021-02-11 14:37:17.000000000 +0000 @@ -31,6 +31,7 @@ #include #include #include +#include "utils.hpp" GrooveSettingsDialog::GrooveSettingsDialog(QWidget *parent) : QDialog(parent), @@ -197,7 +198,7 @@ { size_t id = static_cast(ui->grooveListWidget->currentRow()); auto& ref = seqs_[id]; - if (std::find(ref.begin(), ref.end(), 1) != ref.end()) return; + if (utils::find(ref, 1) != ref.end()) return; std::vector seq; for (auto v : ref) { diff -Nru bambootracker-0.4.5+git20121209/BambooTracker/gui/gui_utils.cpp bambootracker-0.4.6/BambooTracker/gui/gui_utils.cpp --- bambootracker-0.4.5+git20121209/BambooTracker/gui/gui_utils.cpp 2020-12-08 14:05:28.000000000 +0000 +++ bambootracker-0.4.6/BambooTracker/gui/gui_utils.cpp 2021-02-11 14:37:17.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (C) 2020 Rerrah + * Copyright (C) 2020-2021 Rerrah * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation @@ -25,6 +25,7 @@ #include "gui_utils.hpp" #include +#include "song.hpp" namespace gui_utils { diff -Nru bambootracker-0.4.5+git20121209/BambooTracker/gui/gui_utils.hpp bambootracker-0.4.6/BambooTracker/gui/gui_utils.hpp --- bambootracker-0.4.5+git20121209/BambooTracker/gui/gui_utils.hpp 2020-12-08 14:05:28.000000000 +0000 +++ bambootracker-0.4.6/BambooTracker/gui/gui_utils.hpp 2021-02-11 14:37:17.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (C) 2020 Rerrah + * Copyright (C) 2020-2021 Rerrah * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation @@ -30,7 +30,9 @@ #include #include #include -#include "misc.hpp" +#include "bamboo_tracker_defs.hpp" + +enum class SongType; namespace gui_utils { diff -Nru bambootracker-0.4.5+git20121209/BambooTracker/gui/instrument_editor/adpcm_sample_editor.cpp bambootracker-0.4.6/BambooTracker/gui/instrument_editor/adpcm_sample_editor.cpp --- bambootracker-0.4.5+git20121209/BambooTracker/gui/instrument_editor/adpcm_sample_editor.cpp 2020-12-08 14:05:28.000000000 +0000 +++ bambootracker-0.4.6/BambooTracker/gui/instrument_editor/adpcm_sample_editor.cpp 2021-02-11 14:37:17.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (C) 2020 Rerrah + * Copyright (C) 2020-2021 Rerrah * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation @@ -28,6 +28,7 @@ #include #include #include +#include #include #include #include @@ -44,10 +45,13 @@ #include #include #include "chip/codec/ymb_codec.hpp" +#include "instrument/sample_adpcm.hpp" #include "gui/event_guard.hpp" #include "gui/instrument_editor/sample_length_dialog.hpp" #include "gui/instrument_editor/grid_settings_dialog.hpp" #include "gui/file_io_error_message_box.hpp" +#include "gui/instrument_editor/instrument_editor_utils.hpp" +#include "utils.hpp" ADPCMSampleEditor::ADPCMSampleEditor(QWidget *parent) : QWidget(parent), @@ -138,7 +142,7 @@ { const QMimeData* mime = event->mimeData(); if (mime->hasUrls() && mime->urls().length() == 1 - && QFileInfo(mime->urls().first().toLocalFile()).suffix().toLower() == "wav") { + && QFileInfo(mime->urls().at(0).toLocalFile()).suffix().toLower() == "wav") { event->acceptProposedAction(); } } @@ -318,15 +322,19 @@ { std::unique_ptr wav; try { - QFile fp(file); - if (!fp.open(QIODevice::ReadOnly)) { - FileIOErrorMessageBox::openError(file, true, io::FileType::WAV, this); - return; + io::BinaryContainer container; + { + QFile fp(file); + if (!fp.open(QIODevice::ReadOnly)) { + FileIOErrorMessageBox::openError(file, true, io::FileType::WAV, this); + return; + } + QByteArray array = fp.readAll(); + fp.close(); + std::move(array.begin(), array.end(), std::back_inserter(container)); } - QByteArray array = fp.readAll(); - fp.close(); - wav = std::make_unique(io::BinaryContainer(std::vector(array.begin(), array.end()))); + wav = std::make_unique(container); } catch (io::FileIOError& e) { FileIOErrorMessageBox(file, true, e, this).exec(); @@ -359,12 +367,10 @@ std::vector adpcm((raw.size() + 1) / 2); codec::ymb_encode(raw.data(), adpcm.data(), static_cast(raw.size())); - const int ROOT_KEY = 60; //C5 - - bt_.lock()->storeSampleADPCMRawSample(ui->sampleNumSpinBox->value(), adpcm); - ui->rootKeyComboBox->setCurrentIndex(ROOT_KEY % 12); - ui->rootKeySpinBox->setValue(ROOT_KEY / 12); - ui->rootRateSpinBox->setValue(calcADPCMDeltaN(wav->getSampleRate())); + bt_.lock()->storeSampleADPCMRawSample(ui->sampleNumSpinBox->value(), std::move(adpcm)); + ui->rootKeyComboBox->setCurrentIndex(SampleADPCM::DEF_ROOT_KEY % 12); + ui->rootKeySpinBox->setValue(SampleADPCM::DEF_ROOT_KEY / 12); + ui->rootRateSpinBox->setValue(SampleADPCM::calculateADPCMDeltaN(wav->getSampleRate())); emit modified(); emit sampleAssignRequested(); @@ -451,12 +457,8 @@ void ADPCMSampleEditor::updateUsersView() { - std::vector users = bt_.lock()->getSampleADPCMUsers(ui->sampleNumSpinBox->value()); - QStringList l; - std::transform(users.begin(), users.end(), std::back_inserter(l), [](int n) { - return QString("%1").arg(n, 2, 16, QChar('0')).toUpper(); - }); - ui->usersLineEdit->setText(l.join(",")); + std::multiset users = bt_.lock()->getSampleADPCMUsers(ui->sampleNumSpinBox->value()); + ui->usersLineEdit->setText(inst_edit_utils::generateUsersString(users)); } void ADPCMSampleEditor::detectCursorSamplePosition(int cx, int cy) @@ -477,14 +479,14 @@ } else { cursorSamp_.setX( - ui->horizontalScrollBar->value() + (viewedSampLen_ - 1) * clamp(cx, 0, w - 1) / (w - 1)); + ui->horizontalScrollBar->value() + (viewedSampLen_ - 1) * utils::clamp(cx, 0, w - 1) / (w - 1)); } // Detect y const double centerY = rect.height() >> 1; int y = std::numeric_limits::max() * (centerY - cy) / centerY; - cursorSamp_.setY(clamp(y, static_cast(std::numeric_limits::min()), - static_cast(std::numeric_limits::max()))); + cursorSamp_.setY(utils::clamp(y, static_cast(std::numeric_limits::min()), + static_cast(std::numeric_limits::max()))); // Update position view ui->detailLabel->setText(updateDetailView()); diff -Nru bambootracker-0.4.5+git20121209/BambooTracker/gui/instrument_editor/arpeggio_macro_editor.cpp bambootracker-0.4.6/BambooTracker/gui/instrument_editor/arpeggio_macro_editor.cpp --- bambootracker-0.4.5+git20121209/BambooTracker/gui/instrument_editor/arpeggio_macro_editor.cpp 2020-12-08 14:05:28.000000000 +0000 +++ bambootracker-0.4.6/BambooTracker/gui/instrument_editor/arpeggio_macro_editor.cpp 2021-02-11 14:37:17.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (C) 2019-2020 Rerrah + * Copyright (C) 2019-2021 Rerrah * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation @@ -25,8 +25,12 @@ #include "arpeggio_macro_editor.hpp" #include +#include +#include "note.hpp" -const QString ArpeggioMacroEditor::TONE_LABS_[96] = { +namespace +{ +const QString TONE_LABS[96] = { "C-0", "C#0", "D-0", "D#0", "E-0", "F-0", "F#0", "G-0", "G#0", "A-0", "A#0", "B-0", "C-1", "C#1", "D-1", "D#1", "E-1", "F-1", "F#1", "G-1", "G#1", "A-1", "A#1", "B-1", "C-2", "C#2", "D-2", "D#2", "E-2", "F-2", "F#2", "G-2", "G#2", "A-2", "A#2", "B-2", @@ -36,27 +40,33 @@ "C-6", "C#6", "D-6", "D#6", "E-6", "F-6", "F#6", "G-6", "G#6", "A-6", "A#6", "B-6", "C-7", "C#7", "D-7", "D#7", "E-7", "F-7", "F#7", "G-7", "G#7", "A-7", "A#7", "B-7" }; +static_assert (sizeof(TONE_LABS) == sizeof(QString) * Note::NOTE_NUMBER_RANGE, "Invalid note name table size"); + +const QHash NOTE_NAME_NUMS = { + { "C-", 0 }, { "C#", 1 }, { "D-", 2 }, { "D#", 3 }, { "E", 4 }, { "F", 5 }, + { "F#", 6 }, { "G-", 7 }, { "G#", 8 }, { "A-", 9 }, { "A#", 10 }, { "B", 11 } +}; +} ArpeggioMacroEditor::ArpeggioMacroEditor(QWidget* parent) : VisualizedInstrumentMacroEditor(parent) { - setMaximumDisplayedRowCount(15); - setDefaultRow(48); + constexpr int MAX_DISP_CNT = 15; + setMaximumDisplayedRowCount(MAX_DISP_CNT); + setDefaultRow(Note::DEFAULT_NOTE_NUM); setLabelDiaplayMode(true); - for (int i = 0; i < 96; ++i) { - AddRow(QString::asprintf("%+d", i - 48), false); + for (int i = 0; i < Note::NOTE_NUMBER_RANGE; ++i) { + AddRow(QString::asprintf("%+d", i - Note::DEFAULT_NOTE_NUM), false); } autoFitLabelWidth(); - setUpperRow(55); - setMMLDisplay0As(-48); + setUpperRow(Note::DEFAULT_NOTE_NUM + MAX_DISP_CNT / 2); + setMMLDisplay0As(-Note::DEFAULT_NOTE_NUM); setSequenceType(SequenceType::AbsoluteSequence); } -ArpeggioMacroEditor::~ArpeggioMacroEditor() {} - QString ArpeggioMacroEditor::convertSequenceDataUnitToMML(Column col) { - if (type_ == SequenceType::FixedSequence) return TONE_LABS_[col.row]; + if (type_ == SequenceType::FixedSequence) return TONE_LABS[col.row]; else return VisualizedInstrumentMacroEditor::convertSequenceDataUnitToMML(col); } @@ -65,32 +75,8 @@ if (type_ == SequenceType::FixedSequence) { QRegularExpressionMatch m = QRegularExpression("^([A-G][-#])([0-7])").match(text); if (m.hasMatch()) { - QString tone = m.captured(1); int oct = m.captured(2).toInt(); - int d = 12 * oct; - if (tone.left(1) == "C") { - if (tone.right(1) == "-") ; - else d += 1; - } - else if (tone.left(1) == "D") { - if (tone.right(1) == "-") d += 2; - else d += 3; - } - else if (tone.left(1) == "E") d +=4; - else if (tone.left(1) == "F") { - if (tone.right(1) == "-") d += 5; - else d += 6; - } - else if (tone.left(1) == "G") { - if (tone.right(1) == "-") d += 7; - else d += 8; - } - else if (tone.left(1) == "A") { - if (tone.right(1) == "-") d += 9; - else d += 10; - } - else d += 11; // 'B' - + int d = 12 * oct + NOTE_NAME_NUMS[m.captured(1)]; column.push_back({ d, -1, "" }); ++cnt; text.remove(QRegularExpression("^[A-G][-#][0-7]")); @@ -106,11 +92,11 @@ void ArpeggioMacroEditor::updateLabels() { if (type_ == SequenceType::FixedSequence) { - for (int i = 0; i < 96; ++i) - setLabel(i, TONE_LABS_[i]); + for (int i = 0; i < Note::NOTE_NUMBER_RANGE; ++i) + setLabel(i, TONE_LABS[i]); } else { - for (int i = 0; i < 96; ++i) - setLabel(i, QString::asprintf("%+d", i - 48)); + for (int i = 0; i < Note::NOTE_NUMBER_RANGE; ++i) + setLabel(i, QString::asprintf("%+d", i - Note::DEFAULT_NOTE_NUM)); } } diff -Nru bambootracker-0.4.5+git20121209/BambooTracker/gui/instrument_editor/arpeggio_macro_editor.hpp bambootracker-0.4.6/BambooTracker/gui/instrument_editor/arpeggio_macro_editor.hpp --- bambootracker-0.4.5+git20121209/BambooTracker/gui/instrument_editor/arpeggio_macro_editor.hpp 2020-12-08 14:05:28.000000000 +0000 +++ bambootracker-0.4.6/BambooTracker/gui/instrument_editor/arpeggio_macro_editor.hpp 2021-02-11 14:37:17.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (C) 2019 Rerrah + * Copyright (C) 2019-2021 Rerrah * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation @@ -23,8 +23,8 @@ * OTHER DEALINGS IN THE SOFTWARE. */ -#ifndef ARPEGGIOMACROEDITOR_HPP -#define ARPEGGIOMACROEDITOR_HPP +#ifndef ARPEGGIO_MACRO_EDITOR_HPP +#define ARPEGGIO_MACRO_EDITOR_HPP #include "visualized_instrument_macro_editor.hpp" #include @@ -36,16 +36,13 @@ public: ArpeggioMacroEditor(QWidget *parent = nullptr); - ~ArpeggioMacroEditor() override; protected: QString convertSequenceDataUnitToMML(Column col) override; bool interpretDataInMML(QString &text, int &cnt, std::vector &column) override; private: - static const QString TONE_LABS_[96]; - void updateLabels() override; }; -#endif // ARPEGGIOMACROEDITOR_HPP +#endif // ARPEGGIO_MACRO_EDITOR_HPP diff -Nru bambootracker-0.4.5+git20121209/BambooTracker/gui/instrument_editor/fm_operator_table.cpp bambootracker-0.4.6/BambooTracker/gui/instrument_editor/fm_operator_table.cpp --- bambootracker-0.4.5+git20121209/BambooTracker/gui/instrument_editor/fm_operator_table.cpp 2020-12-08 14:05:28.000000000 +0000 +++ bambootracker-0.4.6/BambooTracker/gui/instrument_editor/fm_operator_table.cpp 2021-02-11 14:37:17.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (C) 2018-2020 Rerrah + * Copyright (C) 2018-2021 Rerrah * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation @@ -31,8 +31,11 @@ #include #include "gui/event_guard.hpp" -const int FMOperatorTable::DT_SIGN_TBL_[8] = { 0, 1, 2, 3, 0, -1, -2, -3 }; -const int FMOperatorTable::DT_UNSIGN_TBL_[7] = { 7, 6, 5, 0, 1, 2, 3 }; +namespace +{ +constexpr int DT_SIGN_TBL_[8] = { 0, 1, 2, 3, 0, -1, -2, -3 }; +constexpr int DT_UNSIGN_TBL_[7] = { 7, 6, 5, 0, 1, 2, 3 }; +} FMOperatorTable::FMOperatorTable(QWidget *parent) : QFrame(parent), diff -Nru bambootracker-0.4.5+git20121209/BambooTracker/gui/instrument_editor/fm_operator_table.hpp bambootracker-0.4.6/BambooTracker/gui/instrument_editor/fm_operator_table.hpp --- bambootracker-0.4.5+git20121209/BambooTracker/gui/instrument_editor/fm_operator_table.hpp 2020-12-08 14:05:28.000000000 +0000 +++ bambootracker-0.4.6/BambooTracker/gui/instrument_editor/fm_operator_table.hpp 2021-02-11 14:37:17.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (C) 2018-2020 Rerrah + * Copyright (C) 2018-2021 Rerrah * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation @@ -39,7 +39,6 @@ #include "gui/labeled_vertical_slider.hpp" #include "gui/color_palette.hpp" #include "enum_hash.hpp" -#include "misc.hpp" namespace Ui { class FMOperatorTable; @@ -95,9 +94,6 @@ bool isDTNegative_; bool isIgnoreEvent_; - static const int DT_SIGN_TBL_[8]; - static const int DT_UNSIGN_TBL_[7]; - // Envelope graph QPixmap envmap_; static constexpr int ENV_H_ = 127; diff -Nru bambootracker-0.4.5+git20121209/BambooTracker/gui/instrument_editor/instrument_editor_adpcm_form.cpp bambootracker-0.4.6/BambooTracker/gui/instrument_editor/instrument_editor_adpcm_form.cpp --- bambootracker-0.4.5+git20121209/BambooTracker/gui/instrument_editor/instrument_editor_adpcm_form.cpp 2020-12-08 14:05:28.000000000 +0000 +++ bambootracker-0.4.6/BambooTracker/gui/instrument_editor/instrument_editor_adpcm_form.cpp 2021-02-11 14:37:17.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (C) 2020 Rerrah + * Copyright (C) 2020-2021 Rerrah * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation @@ -25,6 +25,7 @@ #include "instrument_editor_adpcm_form.hpp" #include "ui_instrument_editor_adpcm_form.h" +#include #include "instrument.hpp" #include "gui/event_guard.hpp" #include "gui/jam_layout.hpp" @@ -72,100 +73,140 @@ ui->envEditor->setUpperRow(255); ui->envEditor->setMultipleReleaseState(true); ui->envEditor->setPermittedReleaseTypes( - VisualizedInstrumentMacroEditor::ReleaseType::ABSOLUTE_RELEASE - | VisualizedInstrumentMacroEditor::ReleaseType::RELATIVE_RELEASE - | VisualizedInstrumentMacroEditor::ReleaseType::FIXED_RELEASE); + VisualizedInstrumentMacroEditor::PermittedReleaseFlag::ABSOLUTE_RELEASE + | VisualizedInstrumentMacroEditor::PermittedReleaseFlag::RELATIVE_RELEASE + | VisualizedInstrumentMacroEditor::PermittedReleaseFlag::FIXED_RELEASE); - QObject::connect(ui->envEditor, &VisualizedInstrumentMacroEditor::sequenceCommandAdded, - this, [&](int row, int col) { + QObject::connect(ui->envEditor, &VisualizedInstrumentMacroEditor::sequenceDataAdded, + this, [&](int row, int) { if (!isIgnoreEvent_) { - bt_.lock()->addEnvelopeADPCMSequenceCommand( - ui->envNumSpinBox->value(), row, ui->envEditor->getSequenceDataAt(col)); + bt_.lock()->addEnvelopeADPCMSequenceData(ui->envNumSpinBox->value(), row); emit envelopeParameterChanged(ui->envNumSpinBox->value(), instNum_); emit modified(); } }); - QObject::connect(ui->envEditor, &VisualizedInstrumentMacroEditor::sequenceCommandRemoved, - this, [&]() { + QObject::connect(ui->envEditor, &VisualizedInstrumentMacroEditor::sequenceDataRemoved, + this, [&] { if (!isIgnoreEvent_) { - bt_.lock()->removeEnvelopeADPCMSequenceCommand(ui->envNumSpinBox->value()); + bt_.lock()->removeEnvelopeADPCMSequenceData(ui->envNumSpinBox->value()); emit envelopeParameterChanged(ui->envNumSpinBox->value(), instNum_); emit modified(); } }); - QObject::connect(ui->envEditor, &VisualizedInstrumentMacroEditor::sequenceCommandChanged, + QObject::connect(ui->envEditor, &VisualizedInstrumentMacroEditor::sequenceDataChanged, this, [&](int row, int col) { if (!isIgnoreEvent_) { - bt_.lock()->setEnvelopeADPCMSequenceCommand( - ui->envNumSpinBox->value(), col, row, ui->envEditor->getSequenceDataAt(col)); + bt_.lock()->setEnvelopeADPCMSequenceData(ui->envNumSpinBox->value(), col, row); + emit envelopeParameterChanged(ui->envNumSpinBox->value(), instNum_); + emit modified(); + } + }); + QObject::connect(ui->envEditor, &VisualizedInstrumentMacroEditor::loopAdded, + this, [&](InstrumentSequenceLoop loop) { + if (!isIgnoreEvent_) { + bt_.lock()->addEnvelopeADPCMLoop(ui->envNumSpinBox->value(), loop); + emit envelopeParameterChanged(ui->envNumSpinBox->value(), instNum_); + emit modified(); + } + }); + QObject::connect(ui->envEditor, &VisualizedInstrumentMacroEditor::loopRemoved, + this, [&](int begin, int end) { + if (!isIgnoreEvent_) { + bt_.lock()->removeEnvelopeADPCMLoop(ui->envNumSpinBox->value(), begin, end); + emit envelopeParameterChanged(ui->envNumSpinBox->value(), instNum_); + emit modified(); + } + }); + QObject::connect(ui->envEditor, &VisualizedInstrumentMacroEditor::loopCleared, + this, [&] { + if (!isIgnoreEvent_) { + bt_.lock()->clearEnvelopeADPCMLoops(ui->envNumSpinBox->value()); emit envelopeParameterChanged(ui->envNumSpinBox->value(), instNum_); emit modified(); } }); QObject::connect(ui->envEditor, &VisualizedInstrumentMacroEditor::loopChanged, - this, [&](std::vector begins, std::vector ends, std::vector times) { + this, [&](int prevBegin, int prevEnd, InstrumentSequenceLoop loop) { if (!isIgnoreEvent_) { - bt_.lock()->setEnvelopeADPCMLoops( - ui->envNumSpinBox->value(), std::move(begins), std::move(ends), std::move(times)); + bt_.lock()->changeEnvelopeADPCMLoop(ui->envNumSpinBox->value(), prevBegin, prevEnd, loop); emit envelopeParameterChanged(ui->envNumSpinBox->value(), instNum_); emit modified(); } }); QObject::connect(ui->envEditor, &VisualizedInstrumentMacroEditor::releaseChanged, - this, [&](VisualizedInstrumentMacroEditor::ReleaseType type, int point) { + this, [&](InstrumentSequenceRelease release) { if (!isIgnoreEvent_) { - ReleaseType t = inst_edit_utils::convertReleaseTypeForData(type); - bt_.lock()->setEnvelopeADPCMRelease(ui->envNumSpinBox->value(), t, point); + bt_.lock()->setEnvelopeADPCMRelease(ui->envNumSpinBox->value(), release); emit envelopeParameterChanged(ui->envNumSpinBox->value(), instNum_); emit modified(); } }); //========== Arpeggio ==========// - ui->arpTypeComboBox->addItem(tr("Absolute"), VisualizedInstrumentMacroEditor::SequenceType::AbsoluteSequence); - ui->arpTypeComboBox->addItem(tr("Fixed"), VisualizedInstrumentMacroEditor::SequenceType::FixedSequence); - ui->arpTypeComboBox->addItem(tr("Relative"), VisualizedInstrumentMacroEditor::SequenceType::RelativeSequence); + ui->arpTypeComboBox->addItem(tr("Absolute"), static_cast(SequenceType::AbsoluteSequence)); + ui->arpTypeComboBox->addItem(tr("Fixed"), static_cast(SequenceType::FixedSequence)); + ui->arpTypeComboBox->addItem(tr("Relative"), static_cast(SequenceType::RelativeSequence)); - QObject::connect(ui->arpEditor, &VisualizedInstrumentMacroEditor::sequenceCommandAdded, - this, [&](int row, int col) { + QObject::connect(ui->arpEditor, &VisualizedInstrumentMacroEditor::sequenceDataAdded, + this, [&](int row, int) { if (!isIgnoreEvent_) { - bt_.lock()->addArpeggioADPCMSequenceCommand( - ui->arpNumSpinBox->value(), row, ui->arpEditor->getSequenceDataAt(col)); + bt_.lock()->addArpeggioADPCMSequenceData(ui->arpNumSpinBox->value(), row); emit arpeggioParameterChanged(ui->arpNumSpinBox->value(), instNum_); emit modified(); } }); - QObject::connect(ui->arpEditor, &VisualizedInstrumentMacroEditor::sequenceCommandRemoved, - this, [&]() { + QObject::connect(ui->arpEditor, &VisualizedInstrumentMacroEditor::sequenceDataRemoved, + this, [&] { if (!isIgnoreEvent_) { - bt_.lock()->removeArpeggioADPCMSequenceCommand(ui->arpNumSpinBox->value()); + bt_.lock()->removeArpeggioADPCMSequenceData(ui->arpNumSpinBox->value()); emit arpeggioParameterChanged(ui->arpNumSpinBox->value(), instNum_); emit modified(); } }); - QObject::connect(ui->arpEditor, &VisualizedInstrumentMacroEditor::sequenceCommandChanged, + QObject::connect(ui->arpEditor, &VisualizedInstrumentMacroEditor::sequenceDataChanged, this, [&](int row, int col) { if (!isIgnoreEvent_) { - bt_.lock()->setArpeggioADPCMSequenceCommand( - ui->arpNumSpinBox->value(), col, row, ui->arpEditor->getSequenceDataAt(col)); + bt_.lock()->setArpeggioADPCMSequenceData(ui->arpNumSpinBox->value(), col, row); + emit arpeggioParameterChanged(ui->arpNumSpinBox->value(), instNum_); + emit modified(); + } + }); + QObject::connect(ui->arpEditor, &VisualizedInstrumentMacroEditor::loopAdded, + this, [&](InstrumentSequenceLoop loop) { + if (!isIgnoreEvent_) { + bt_.lock()->addArpeggioADPCMLoop(ui->arpNumSpinBox->value(), loop); + emit arpeggioParameterChanged(ui->arpNumSpinBox->value(), instNum_); + emit modified(); + } + }); + QObject::connect(ui->arpEditor, &VisualizedInstrumentMacroEditor::loopRemoved, + this, [&](int begin, int end) { + if (!isIgnoreEvent_) { + bt_.lock()->removeArpeggioADPCMLoop(ui->arpNumSpinBox->value(), begin, end); + emit arpeggioParameterChanged(ui->arpNumSpinBox->value(), instNum_); + emit modified(); + } + }); + QObject::connect(ui->arpEditor, &VisualizedInstrumentMacroEditor::loopCleared, + this, [&] { + if (!isIgnoreEvent_) { + bt_.lock()->clearArpeggioADPCMLoops(ui->arpNumSpinBox->value()); emit arpeggioParameterChanged(ui->arpNumSpinBox->value(), instNum_); emit modified(); } }); QObject::connect(ui->arpEditor, &VisualizedInstrumentMacroEditor::loopChanged, - this, [&](std::vector begins, std::vector ends, std::vector times) { + this, [&](int prevBegin, int prevEnd, InstrumentSequenceLoop loop) { if (!isIgnoreEvent_) { - bt_.lock()->setArpeggioADPCMLoops( - ui->arpNumSpinBox->value(), std::move(begins), std::move(ends), std::move(times)); + bt_.lock()->changeArpeggioADPCMLoop(ui->arpNumSpinBox->value(), prevBegin, prevEnd, loop); emit arpeggioParameterChanged(ui->arpNumSpinBox->value(), instNum_); emit modified(); } }); QObject::connect(ui->arpEditor, &VisualizedInstrumentMacroEditor::releaseChanged, - this, [&](VisualizedInstrumentMacroEditor::ReleaseType type, int point) { + this, [&](InstrumentSequenceRelease release) { if (!isIgnoreEvent_) { - ReleaseType t = inst_edit_utils::convertReleaseTypeForData(type); - bt_.lock()->setArpeggioADPCMRelease(ui->arpNumSpinBox->value(), t, point); + bt_.lock()->setArpeggioADPCMRelease(ui->arpNumSpinBox->value(), release); emit arpeggioParameterChanged(ui->arpNumSpinBox->value(), instNum_); emit modified(); } @@ -185,49 +226,69 @@ ui->ptEditor->setUpperRow(134); ui->ptEditor->setMMLDisplay0As(-127); - ui->ptTypeComboBox->addItem(tr("Absolute"), VisualizedInstrumentMacroEditor::SequenceType::AbsoluteSequence); - ui->ptTypeComboBox->addItem(tr("Relative"), VisualizedInstrumentMacroEditor::SequenceType::RelativeSequence); + ui->ptTypeComboBox->addItem(tr("Absolute"), static_cast(SequenceType::AbsoluteSequence)); + ui->ptTypeComboBox->addItem(tr("Relative"), static_cast(SequenceType::RelativeSequence)); - QObject::connect(ui->ptEditor, &VisualizedInstrumentMacroEditor::sequenceCommandAdded, - this, [&](int row, int col) { + QObject::connect(ui->ptEditor, &VisualizedInstrumentMacroEditor::sequenceDataAdded, + this, [&](int row, int) { if (!isIgnoreEvent_) { - bt_.lock()->addPitchADPCMSequenceCommand( - ui->ptNumSpinBox->value(), row, ui->ptEditor->getSequenceDataAt(col)); + bt_.lock()->addPitchADPCMSequenceData(ui->ptNumSpinBox->value(), row); emit pitchParameterChanged(ui->ptNumSpinBox->value(), instNum_); emit modified(); } }); - QObject::connect(ui->ptEditor, &VisualizedInstrumentMacroEditor::sequenceCommandRemoved, - this, [&]() { + QObject::connect(ui->ptEditor, &VisualizedInstrumentMacroEditor::sequenceDataRemoved, + this, [&] { if (!isIgnoreEvent_) { - bt_.lock()->removePitchADPCMSequenceCommand(ui->ptNumSpinBox->value()); + bt_.lock()->removePitchADPCMSequenceData(ui->ptNumSpinBox->value()); emit pitchParameterChanged(ui->ptNumSpinBox->value(), instNum_); emit modified(); } }); - QObject::connect(ui->ptEditor, &VisualizedInstrumentMacroEditor::sequenceCommandChanged, + QObject::connect(ui->ptEditor, &VisualizedInstrumentMacroEditor::sequenceDataChanged, this, [&](int row, int col) { if (!isIgnoreEvent_) { - bt_.lock()->setPitchADPCMSequenceCommand( - ui->ptNumSpinBox->value(), col, row, ui->ptEditor->getSequenceDataAt(col)); + bt_.lock()->setPitchADPCMSequenceData(ui->ptNumSpinBox->value(), col, row); + emit pitchParameterChanged(ui->ptNumSpinBox->value(), instNum_); + emit modified(); + } + }); + QObject::connect(ui->ptEditor, &VisualizedInstrumentMacroEditor::loopAdded, + this, [&](InstrumentSequenceLoop loop) { + if (!isIgnoreEvent_) { + bt_.lock()->addPitchADPCMLoop(ui->ptNumSpinBox->value(), loop); + emit pitchParameterChanged(ui->ptNumSpinBox->value(), instNum_); + emit modified(); + } + }); + QObject::connect(ui->ptEditor, &VisualizedInstrumentMacroEditor::loopRemoved, + this, [&](int begin, int end) { + if (!isIgnoreEvent_) { + bt_.lock()->removePitchADPCMLoop(ui->ptNumSpinBox->value(), begin, end); + emit pitchParameterChanged(ui->ptNumSpinBox->value(), instNum_); + emit modified(); + } + }); + QObject::connect(ui->ptEditor, &VisualizedInstrumentMacroEditor::loopCleared, + this, [&] { + if (!isIgnoreEvent_) { + bt_.lock()->clearPitchADPCMLoops(ui->ptNumSpinBox->value()); emit pitchParameterChanged(ui->ptNumSpinBox->value(), instNum_); emit modified(); } }); QObject::connect(ui->ptEditor, &VisualizedInstrumentMacroEditor::loopChanged, - this, [&](std::vector begins, std::vector ends, std::vector times) { + this, [&](int prevBegin, int prevEnd, InstrumentSequenceLoop loop) { if (!isIgnoreEvent_) { - bt_.lock()->setPitchADPCMLoops( - ui->ptNumSpinBox->value(), std::move(begins), std::move(ends), std::move(times)); + bt_.lock()->changePitchADPCMLoop(ui->ptNumSpinBox->value(), prevBegin, prevEnd, loop); emit pitchParameterChanged(ui->ptNumSpinBox->value(), instNum_); emit modified(); } }); QObject::connect(ui->ptEditor, &VisualizedInstrumentMacroEditor::releaseChanged, - this, [&](VisualizedInstrumentMacroEditor::ReleaseType type, int point) { + this, [&](InstrumentSequenceRelease release) { if (!isIgnoreEvent_) { - ReleaseType t = inst_edit_utils::convertReleaseTypeForData(type); - bt_.lock()->setPitchADPCMRelease(ui->ptNumSpinBox->value(), t, point); + bt_.lock()->setPitchADPCMRelease(ui->ptNumSpinBox->value(), release); emit pitchParameterChanged(ui->ptNumSpinBox->value(), instNum_); emit modified(); } @@ -367,14 +428,13 @@ ui->envNumSpinBox->setValue(instADPCM->getEnvelopeNumber()); ui->envEditor->clearData(); - for (auto& com : instADPCM->getEnvelopeSequence()) { - ui->envEditor->addSequenceCommand(com.type); + for (auto& unit : instADPCM->getEnvelopeSequence()) { + ui->envEditor->addSequenceData(unit.data); } - for (auto& l : instADPCM->getEnvelopeLoops()) { - ui->envEditor->addLoop(l.begin, l.end, l.times); + for (auto& loop : instADPCM->getEnvelopeLoopRoot().getAllLoops()) { + ui->envEditor->addLoop(loop.getBeginPos(), loop.getEndPos(), loop.getTimes()); } - ui->envEditor->setRelease(inst_edit_utils::convertReleaseTypeForUI(instADPCM->getEnvelopeRelease().type), - instADPCM->getEnvelopeRelease().begin); + ui->envEditor->setRelease(instADPCM->getEnvelopeRelease()); if (instADPCM->getEnvelopeEnabled()) { ui->envEditGroupBox->setChecked(true); onEnvelopeNumberChanged(); @@ -388,12 +448,8 @@ void InstrumentEditorADPCMForm::onEnvelopeNumberChanged() { // Change users view - std::vector users = bt_.lock()->getEnvelopeADPCMUsers(ui->envNumSpinBox->value()); - QStringList l; - std::transform(users.begin(), users.end(), std::back_inserter(l), [](int n) { - return QString("%1").arg(n, 2, 16, QChar('0')).toUpper(); - }); - ui->envUsersLineEdit->setText(l.join((","))); + std::multiset users = bt_.lock()->getEnvelopeADPCMUsers(ui->envNumSpinBox->value()); + ui->envUsersLineEdit->setText(inst_edit_utils::generateUsersString(users)); } void InstrumentEditorADPCMForm::onEnvelopeParameterChanged(int envNum) @@ -438,17 +494,15 @@ ui->arpNumSpinBox->setValue(instADPCM->getArpeggioNumber()); ui->arpEditor->clearData(); - for (auto& com : instADPCM->getArpeggioSequence()) { - ui->arpEditor->addSequenceCommand(com.type); + for (auto& unit : instADPCM->getArpeggioSequence()) { + ui->arpEditor->addSequenceData(unit.data); } - for (auto& l : instADPCM->getArpeggioLoops()) { - ui->arpEditor->addLoop(l.begin, l.end, l.times); + for (auto& loop : instADPCM->getArpeggioLoopRoot().getAllLoops()) { + ui->arpEditor->addLoop(loop.getBeginPos(), loop.getEndPos(), loop.getTimes()); } - ui->arpEditor->setRelease(inst_edit_utils::convertReleaseTypeForUI(instADPCM->getArpeggioRelease().type), - instADPCM->getArpeggioRelease().begin); + ui->arpEditor->setRelease(instADPCM->getArpeggioRelease()); for (int i = 0; i < ui->arpTypeComboBox->count(); ++i) { - if (instADPCM->getArpeggioType() == inst_edit_utils::convertSequenceTypeForData( - static_cast(ui->arpTypeComboBox->itemData(i).toInt()))) { + if (instADPCM->getArpeggioType() == static_cast(ui->arpTypeComboBox->itemData(i).toInt())) { ui->arpTypeComboBox->setCurrentIndex(i); break; } @@ -466,12 +520,8 @@ void InstrumentEditorADPCMForm::onArpeggioNumberChanged() { // Change users view - std::vector users = bt_.lock()->getArpeggioADPCMUsers(ui->arpNumSpinBox->value()); - QStringList l; - std::transform(users.begin(), users.end(), std::back_inserter(l), [](int n) { - return QString("%1").arg(n, 2, 16, QChar('0')).toUpper(); - }); - ui->arpUsersLineEdit->setText(l.join(",")); + std::multiset users = bt_.lock()->getArpeggioADPCMUsers(ui->arpNumSpinBox->value()); + ui->arpUsersLineEdit->setText(inst_edit_utils::generateUsersString(users)); } void InstrumentEditorADPCMForm::onArpeggioParameterChanged(int tnNum) @@ -486,10 +536,9 @@ { Q_UNUSED(index) - auto type = static_cast( - ui->arpTypeComboBox->currentData(Qt::UserRole).toInt()); + auto type = static_cast(ui->arpTypeComboBox->currentData(Qt::UserRole).toInt()); if (!isIgnoreEvent_) { - bt_.lock()->setArpeggioADPCMType(ui->arpNumSpinBox->value(), inst_edit_utils::convertSequenceTypeForData(type)); + bt_.lock()->setArpeggioADPCMType(ui->arpNumSpinBox->value(), type); emit arpeggioParameterChanged(ui->arpNumSpinBox->value(), instNum_); emit modified(); } @@ -531,17 +580,15 @@ ui->ptNumSpinBox->setValue(instADPCM->getPitchNumber()); ui->ptEditor->clearData(); - for (auto& com : instADPCM->getPitchSequence()) { - ui->ptEditor->addSequenceCommand(com.type); + for (auto& unit : instADPCM->getPitchSequence()) { + ui->ptEditor->addSequenceData(unit.data); } - for (auto& l : instADPCM->getPitchLoops()) { - ui->ptEditor->addLoop(l.begin, l.end, l.times); + for (auto& loop : instADPCM->getPitchLoopRoot().getAllLoops()) { + ui->ptEditor->addLoop(loop.getBeginPos(), loop.getEndPos(), loop.getTimes()); } - ui->ptEditor->setRelease(inst_edit_utils::convertReleaseTypeForUI(instADPCM->getPitchRelease().type), - instADPCM->getPitchRelease().begin); + ui->ptEditor->setRelease(instADPCM->getPitchRelease()); for (int i = 0; i < ui->ptTypeComboBox->count(); ++i) { - if (instADPCM->getPitchType() == inst_edit_utils::convertSequenceTypeForData( - static_cast(ui->ptTypeComboBox->itemData(i).toInt()))) { + if (instADPCM->getPitchType() == static_cast(ui->ptTypeComboBox->itemData(i).toInt())) { ui->ptTypeComboBox->setCurrentIndex(i); break; } @@ -559,12 +606,8 @@ void InstrumentEditorADPCMForm::onPitchNumberChanged() { // Change users view - std::vector users = bt_.lock()->getPitchADPCMUsers(ui->ptNumSpinBox->value()); - QStringList l; - std::transform(users.begin(), users.end(), std::back_inserter(l), [](int n) { - return QString("%1").arg(n, 2, 16, QChar('0')).toUpper(); - }); - ui->ptUsersLineEdit->setText(l.join(",")); + std::multiset users = bt_.lock()->getPitchADPCMUsers(ui->ptNumSpinBox->value()); + ui->ptUsersLineEdit->setText(inst_edit_utils::generateUsersString(users)); } void InstrumentEditorADPCMForm::onPitchParameterChanged(int tnNum) @@ -579,9 +622,9 @@ { Q_UNUSED(index) - auto type = static_cast(ui->ptTypeComboBox->currentData(Qt::UserRole).toInt()); + auto type = static_cast(ui->ptTypeComboBox->currentData(Qt::UserRole).toInt()); if (!isIgnoreEvent_) { - bt_.lock()->setPitchADPCMType(ui->ptNumSpinBox->value(), inst_edit_utils::convertSequenceTypeForData(type)); + bt_.lock()->setPitchADPCMType(ui->ptNumSpinBox->value(), type); emit pitchParameterChanged(ui->ptNumSpinBox->value(), instNum_); emit modified(); } diff -Nru bambootracker-0.4.5+git20121209/BambooTracker/gui/instrument_editor/instrument_editor_drumkit_form.cpp bambootracker-0.4.6/BambooTracker/gui/instrument_editor/instrument_editor_drumkit_form.cpp --- bambootracker-0.4.5+git20121209/BambooTracker/gui/instrument_editor/instrument_editor_drumkit_form.cpp 2020-12-08 14:05:28.000000000 +0000 +++ bambootracker-0.4.6/BambooTracker/gui/instrument_editor/instrument_editor_drumkit_form.cpp 2021-02-11 14:37:17.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (C) 2020 Rerrah + * Copyright (C) 2020-2021 Rerrah * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation @@ -27,6 +27,7 @@ #include "ui_instrument_editor_drumkit_form.h" #include #include "instrument.hpp" +#include "note.hpp" #include "gui/event_guard.hpp" #include "gui/jam_layout.hpp" #include "gui/gui_utils.hpp" @@ -42,7 +43,7 @@ ui->keyTreeWidget->header()->setSectionResizeMode(QHeaderView::ResizeToContents); QString tone[] = { "C-", "C#", "D-", "D#", "E-", "F-", "F#", "G-", "G#", "A-", "A#", "B-" }; - for (int i = 0; i < 96; ++i) { + for (int i = 0; i < Note::NOTE_NUMBER_RANGE; ++i) { ui->keyTreeWidget->addTopLevelItem( new QTreeWidgetItem({ tone[i % 12] + QString::number(i / 12), "-", "-" })); } @@ -122,8 +123,8 @@ void InstrumentEditorDrumkitForm::showEvent(QShowEvent*) { if (!hasShown_) { - ui->keyTreeWidget->setCurrentItem(ui->keyTreeWidget->topLevelItem(48)); - ui->keyTreeWidget->scrollTo(ui->keyTreeWidget->model()->index(48, 0), + ui->keyTreeWidget->setCurrentItem(ui->keyTreeWidget->topLevelItem(Note::DEFAULT_NOTE_NUM)); + ui->keyTreeWidget->scrollTo(ui->keyTreeWidget->model()->index(Note::DEFAULT_NOTE_NUM, 0), QAbstractItemView::PositionAtTop); } hasShown_ = true; diff -Nru bambootracker-0.4.5+git20121209/BambooTracker/gui/instrument_editor/instrument_editor_fm_form.cpp bambootracker-0.4.6/BambooTracker/gui/instrument_editor/instrument_editor_fm_form.cpp --- bambootracker-0.4.5+git20121209/BambooTracker/gui/instrument_editor/instrument_editor_fm_form.cpp 2020-12-08 14:05:28.000000000 +0000 +++ bambootracker-0.4.6/BambooTracker/gui/instrument_editor/instrument_editor_fm_form.cpp 2021-02-11 14:37:17.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (C) 2018-2020 Rerrah + * Copyright (C) 2018-2021 Rerrah * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation @@ -37,10 +37,10 @@ #include #include #include +#include #include "gui/event_guard.hpp" #include "gui/jam_layout.hpp" #include "gui/instrument_editor/instrument_editor_utils.hpp" -#include "misc.hpp" #include "gui/gui_utils.hpp" InstrumentEditorFMForm::InstrumentEditorFMForm(int num, QWidget *parent) : @@ -368,51 +368,76 @@ ui->opSeqEditor->setLabelDiaplayMode(true); setOperatorSequenceEditor(); - QObject::connect(ui->opSeqEditor, &VisualizedInstrumentMacroEditor::sequenceCommandAdded, - this, [&](int row, int col) { + QObject::connect(ui->opSeqEditor, &VisualizedInstrumentMacroEditor::sequenceDataAdded, + this, [&](int row, int) { if (!isIgnoreEvent_) { FMEnvelopeParameter param = getOperatorSequenceParameter(); - bt_.lock()->addOperatorSequenceFMSequenceCommand( - param, ui->opSeqNumSpinBox->value(), row, ui->opSeqEditor->getSequenceDataAt(col)); + bt_.lock()->addOperatorSequenceFMSequenceData(param, ui->opSeqNumSpinBox->value(), row); emit operatorSequenceParameterChanged(param, ui->opSeqNumSpinBox->value(), instNum_); emit modified(); } }); - QObject::connect(ui->opSeqEditor, &VisualizedInstrumentMacroEditor::sequenceCommandRemoved, - this, [&]() { + QObject::connect(ui->opSeqEditor, &VisualizedInstrumentMacroEditor::sequenceDataRemoved, + this, [&] { if (!isIgnoreEvent_) { FMEnvelopeParameter param = getOperatorSequenceParameter(); - bt_.lock()->removeOperatorSequenceFMSequenceCommand(param, ui->opSeqNumSpinBox->value()); + bt_.lock()->removeOperatorSequenceFMSequenceData(param, ui->opSeqNumSpinBox->value()); emit operatorSequenceParameterChanged(param, ui->opSeqNumSpinBox->value(), instNum_); emit modified(); } }); - QObject::connect(ui->opSeqEditor, &VisualizedInstrumentMacroEditor::sequenceCommandChanged, + QObject::connect(ui->opSeqEditor, &VisualizedInstrumentMacroEditor::sequenceDataChanged, this, [&](int row, int col) { if (!isIgnoreEvent_) { FMEnvelopeParameter param = getOperatorSequenceParameter(); - bt_.lock()->setOperatorSequenceFMSequenceCommand( - param, ui->opSeqNumSpinBox->value(), col, row, ui->opSeqEditor->getSequenceDataAt(col)); + bt_.lock()->setOperatorSequenceFMSequenceData(param, ui->opSeqNumSpinBox->value(), col, row); + emit operatorSequenceParameterChanged(param, ui->opSeqNumSpinBox->value(), instNum_); + emit modified(); + } + }); + QObject::connect(ui->opSeqEditor, &VisualizedInstrumentMacroEditor::loopAdded, + this, [&](InstrumentSequenceLoop loop) { + if (!isIgnoreEvent_) { + FMEnvelopeParameter param = getOperatorSequenceParameter(); + bt_.lock()->addOperatorSequenceFMLoop(param, ui->opSeqNumSpinBox->value(), loop); + emit operatorSequenceParameterChanged(param, ui->opSeqNumSpinBox->value(), instNum_); + emit modified(); + } + }); + QObject::connect(ui->opSeqEditor, &VisualizedInstrumentMacroEditor::loopRemoved, + this, [&](int begin, int end) { + if (!isIgnoreEvent_) { + FMEnvelopeParameter param = getOperatorSequenceParameter(); + bt_.lock()->removeOperatorSequenceFMLoop( + param, ui->opSeqNumSpinBox->value(), begin, end); + emit operatorSequenceParameterChanged(param, ui->opSeqNumSpinBox->value(), instNum_); + emit modified(); + } + }); + QObject::connect(ui->opSeqEditor, &VisualizedInstrumentMacroEditor::loopCleared, + this, [&] { + if (!isIgnoreEvent_) { + FMEnvelopeParameter param = getOperatorSequenceParameter(); + bt_.lock()->clearOperatorSequenceFMLoops(param, ui->opSeqNumSpinBox->value()); emit operatorSequenceParameterChanged(param, ui->opSeqNumSpinBox->value(), instNum_); emit modified(); } }); QObject::connect(ui->opSeqEditor, &VisualizedInstrumentMacroEditor::loopChanged, - this, [&](std::vector begins, std::vector ends, std::vector times) { + this, [&](int prevBegin, int prevEnd, InstrumentSequenceLoop loop) { if (!isIgnoreEvent_) { FMEnvelopeParameter param = getOperatorSequenceParameter(); - bt_.lock()->setOperatorSequenceFMLoops( - param, ui->opSeqNumSpinBox->value(), std::move(begins), std::move(ends), std::move(times)); + bt_.lock()->changeOperatorSequenceFMLoop( + param, ui->opSeqNumSpinBox->value(), prevBegin, prevEnd, loop); emit operatorSequenceParameterChanged(param, ui->opSeqNumSpinBox->value(), instNum_); emit modified(); } }); QObject::connect(ui->opSeqEditor, &VisualizedInstrumentMacroEditor::releaseChanged, - this, [&](VisualizedInstrumentMacroEditor::ReleaseType type, int point) { + this, [&](InstrumentSequenceRelease release) { if (!isIgnoreEvent_) { FMEnvelopeParameter param = getOperatorSequenceParameter(); - ReleaseType t = inst_edit_utils::convertReleaseTypeForData(type); - bt_.lock()->setOperatorSequenceFMRelease(param, ui->opSeqNumSpinBox->value(), t, point); + bt_.lock()->setOperatorSequenceFMRelease(param, ui->opSeqNumSpinBox->value(), release); emit operatorSequenceParameterChanged(param, ui->opSeqNumSpinBox->value(), instNum_); emit modified(); } @@ -428,50 +453,70 @@ ui->arpOpComboBox->addItem("Op3", static_cast(FMOperatorType::Op3)); ui->arpOpComboBox->addItem("Op4", static_cast(FMOperatorType::Op4)); - ui->arpTypeComboBox->addItem(tr("Absolute"), VisualizedInstrumentMacroEditor::SequenceType::AbsoluteSequence); - ui->arpTypeComboBox->addItem(tr("Fixed"), VisualizedInstrumentMacroEditor::SequenceType::FixedSequence); - ui->arpTypeComboBox->addItem(tr("Relative"), VisualizedInstrumentMacroEditor::SequenceType::RelativeSequence); + ui->arpTypeComboBox->addItem(tr("Absolute"), static_cast(SequenceType::AbsoluteSequence)); + ui->arpTypeComboBox->addItem(tr("Fixed"), static_cast(SequenceType::FixedSequence)); + ui->arpTypeComboBox->addItem(tr("Relative"), static_cast(SequenceType::RelativeSequence)); - QObject::connect(ui->arpEditor, &VisualizedInstrumentMacroEditor::sequenceCommandAdded, - this, [&](int row, int col) { + QObject::connect(ui->arpEditor, &VisualizedInstrumentMacroEditor::sequenceDataAdded, + this, [&](int row, int) { if (!isIgnoreEvent_) { - bt_.lock()->addArpeggioFMSequenceCommand( - ui->arpNumSpinBox->value(), row, ui->arpEditor->getSequenceDataAt(col)); + bt_.lock()->addArpeggioFMSequenceData(ui->arpNumSpinBox->value(), row); emit arpeggioParameterChanged(ui->arpNumSpinBox->value(), instNum_); emit modified(); } }); - QObject::connect(ui->arpEditor, &VisualizedInstrumentMacroEditor::sequenceCommandRemoved, - this, [&]() { + QObject::connect(ui->arpEditor, &VisualizedInstrumentMacroEditor::sequenceDataRemoved, + this, [&] { if (!isIgnoreEvent_) { - bt_.lock()->removeArpeggioFMSequenceCommand(ui->arpNumSpinBox->value()); + bt_.lock()->removeArpeggioFMSequenceData(ui->arpNumSpinBox->value()); emit arpeggioParameterChanged(ui->arpNumSpinBox->value(), instNum_); emit modified(); } }); - QObject::connect(ui->arpEditor, &VisualizedInstrumentMacroEditor::sequenceCommandChanged, + QObject::connect(ui->arpEditor, &VisualizedInstrumentMacroEditor::sequenceDataChanged, this, [&](int row, int col) { if (!isIgnoreEvent_) { - bt_.lock()->setArpeggioFMSequenceCommand( - ui->arpNumSpinBox->value(), col, row, ui->arpEditor->getSequenceDataAt(col)); + bt_.lock()->setArpeggioFMSequenceData(ui->arpNumSpinBox->value(), col, row); + emit arpeggioParameterChanged(ui->arpNumSpinBox->value(), instNum_); + emit modified(); + } + }); + QObject::connect(ui->arpEditor, &VisualizedInstrumentMacroEditor::loopAdded, + this, [&](InstrumentSequenceLoop loop) { + if (!isIgnoreEvent_) { + bt_.lock()->addArpeggioFMLoop(ui->arpNumSpinBox->value(), loop); + emit arpeggioParameterChanged(ui->arpNumSpinBox->value(), instNum_); + emit modified(); + } + }); + QObject::connect(ui->arpEditor, &VisualizedInstrumentMacroEditor::loopRemoved, + this, [&](int begin, int end) { + if (!isIgnoreEvent_) { + bt_.lock()->removeArpeggioFMLoop(ui->arpNumSpinBox->value(), begin, end); + emit arpeggioParameterChanged(ui->arpNumSpinBox->value(), instNum_); + emit modified(); + } + }); + QObject::connect(ui->arpEditor, &VisualizedInstrumentMacroEditor::loopCleared, + this, [&] { + if (!isIgnoreEvent_) { + bt_.lock()->clearArpeggioFMLoops(ui->arpNumSpinBox->value()); emit arpeggioParameterChanged(ui->arpNumSpinBox->value(), instNum_); emit modified(); } }); QObject::connect(ui->arpEditor, &VisualizedInstrumentMacroEditor::loopChanged, - this, [&](std::vector begins, std::vector ends, std::vector times) { + this, [&](int prevBegin, int prevEnd, InstrumentSequenceLoop loop) { if (!isIgnoreEvent_) { - bt_.lock()->setArpeggioFMLoops( - ui->arpNumSpinBox->value(), std::move(begins), std::move(ends), std::move(times)); + bt_.lock()->changeArpeggioFMLoop(ui->arpNumSpinBox->value(), prevBegin, prevEnd, loop); emit arpeggioParameterChanged(ui->arpNumSpinBox->value(), instNum_); emit modified(); } }); QObject::connect(ui->arpEditor, &VisualizedInstrumentMacroEditor::releaseChanged, - this, [&](VisualizedInstrumentMacroEditor::ReleaseType type, int point) { + this, [&](InstrumentSequenceRelease release) { if (!isIgnoreEvent_) { - ReleaseType t = inst_edit_utils::convertReleaseTypeForData(type); - bt_.lock()->setArpeggioFMRelease(ui->arpNumSpinBox->value(), t, point); + bt_.lock()->setArpeggioFMRelease(ui->arpNumSpinBox->value(), release); emit arpeggioParameterChanged(ui->arpNumSpinBox->value(), instNum_); emit modified(); } @@ -500,49 +545,69 @@ ui->ptEditor->setUpperRow(134); ui->ptEditor->setMMLDisplay0As(-127); - ui->ptTypeComboBox->addItem(tr("Absolute"), VisualizedInstrumentMacroEditor::SequenceType::AbsoluteSequence); - ui->ptTypeComboBox->addItem(tr("Relative"), VisualizedInstrumentMacroEditor::SequenceType::RelativeSequence); + ui->ptTypeComboBox->addItem(tr("Absolute"), static_cast(SequenceType::AbsoluteSequence)); + ui->ptTypeComboBox->addItem(tr("Relative"), static_cast(SequenceType::RelativeSequence)); - QObject::connect(ui->ptEditor, &VisualizedInstrumentMacroEditor::sequenceCommandAdded, - this, [&](int row, int col) { + QObject::connect(ui->ptEditor, &VisualizedInstrumentMacroEditor::sequenceDataAdded, + this, [&](int row, int) { if (!isIgnoreEvent_) { - bt_.lock()->addPitchFMSequenceCommand( - ui->ptNumSpinBox->value(), row, ui->ptEditor->getSequenceDataAt(col)); + bt_.lock()->addPitchFMSequenceData(ui->ptNumSpinBox->value(), row); emit pitchParameterChanged(ui->ptNumSpinBox->value(), instNum_); emit modified(); } }); - QObject::connect(ui->ptEditor, &VisualizedInstrumentMacroEditor::sequenceCommandRemoved, + QObject::connect(ui->ptEditor, &VisualizedInstrumentMacroEditor::sequenceDataRemoved, this, [&]() { if (!isIgnoreEvent_) { - bt_.lock()->removePitchFMSequenceCommand(ui->ptNumSpinBox->value()); + bt_.lock()->removePitchFMSequenceData(ui->ptNumSpinBox->value()); emit pitchParameterChanged(ui->ptNumSpinBox->value(), instNum_); emit modified(); } }); - QObject::connect(ui->ptEditor, &VisualizedInstrumentMacroEditor::sequenceCommandChanged, + QObject::connect(ui->ptEditor, &VisualizedInstrumentMacroEditor::sequenceDataChanged, this, [&](int row, int col) { if (!isIgnoreEvent_) { - bt_.lock()->setPitchFMSequenceCommand( - ui->ptNumSpinBox->value(), col, row, ui->ptEditor->getSequenceDataAt(col)); + bt_.lock()->setPitchFMSequenceData(ui->ptNumSpinBox->value(), col, row); + emit pitchParameterChanged(ui->ptNumSpinBox->value(), instNum_); + emit modified(); + } + }); + QObject::connect(ui->ptEditor, &VisualizedInstrumentMacroEditor::loopAdded, + this, [&](InstrumentSequenceLoop loop) { + if (!isIgnoreEvent_) { + bt_.lock()->addPitchFMLoop(ui->ptNumSpinBox->value(), loop); + emit pitchParameterChanged(ui->ptNumSpinBox->value(), instNum_); + emit modified(); + } + }); + QObject::connect(ui->ptEditor, &VisualizedInstrumentMacroEditor::loopRemoved, + this, [&](int begin, int end) { + if (!isIgnoreEvent_) { + bt_.lock()->removePitchFMLoop(ui->ptNumSpinBox->value(), begin, end); + emit pitchParameterChanged(ui->ptNumSpinBox->value(), instNum_); + emit modified(); + } + }); + QObject::connect(ui->ptEditor, &VisualizedInstrumentMacroEditor::loopCleared, + this, [&] { + if (!isIgnoreEvent_) { + bt_.lock()->clearPitchFMLoops(ui->ptNumSpinBox->value()); emit pitchParameterChanged(ui->ptNumSpinBox->value(), instNum_); emit modified(); } }); QObject::connect(ui->ptEditor, &VisualizedInstrumentMacroEditor::loopChanged, - this, [&](std::vector begins, std::vector ends, std::vector times) { + this, [&](int prevBegin, int prevEnd, InstrumentSequenceLoop loop) { if (!isIgnoreEvent_) { - bt_.lock()->setPitchFMLoops( - ui->ptNumSpinBox->value(), std::move(begins), std::move(ends), std::move(times)); + bt_.lock()->changePitchFMLoop(ui->ptNumSpinBox->value(), prevBegin, prevEnd, loop); emit pitchParameterChanged(ui->ptNumSpinBox->value(), instNum_); emit modified(); } }); QObject::connect(ui->ptEditor, &VisualizedInstrumentMacroEditor::releaseChanged, - this, [&](VisualizedInstrumentMacroEditor::ReleaseType type, int point) { + this, [&](InstrumentSequenceRelease release) { if (!isIgnoreEvent_) { - ReleaseType t = inst_edit_utils::convertReleaseTypeForData(type); - bt_.lock()->setPitchFMRelease(ui->ptNumSpinBox->value(), t, point); + bt_.lock()->setPitchFMRelease(ui->ptNumSpinBox->value(), release); emit pitchParameterChanged(ui->ptNumSpinBox->value(), instNum_); emit modified(); } @@ -1177,12 +1242,9 @@ void InstrumentEditorFMForm::copyEnvelope() { QApplication::clipboard()->setText(QString("FM_ENVELOPE:%1,%2,\n%3,\n%4,\n%5,\n%6,") - .arg(ui->fbSlider->value()) - .arg(ui->alSlider->value()) - .arg(ui->op1Table->toString()) - .arg(ui->op2Table->toString()) - .arg(ui->op3Table->toString()) - .arg(ui->op4Table->toString())); + .arg(ui->fbSlider->value()).arg(ui->alSlider->value()) + .arg(ui->op1Table->toString(), ui->op2Table->toString(), + ui->op3Table->toString(), ui->op4Table->toString())); } void InstrumentEditorFMForm::pasteEnvelope() @@ -1260,12 +1322,8 @@ void InstrumentEditorFMForm::onEnvelopeNumberChanged() { // Change users view - std::vector users = bt_.lock()->getEnvelopeFMUsers(ui->envNumSpinBox->value()); - QStringList l; - std::transform(users.begin(), users.end(), std::back_inserter(l), [](int n) { - return QString("%1").arg(n, 2, 16, QChar('0')).toUpper(); - }); - ui->envUsersLineEdit->setText(l.join((","))); + std::multiset users = bt_.lock()->getEnvelopeFMUsers(ui->envNumSpinBox->value()); + ui->envUsersLineEdit->setText(inst_edit_utils::generateUsersString(users)); } //========== LFO ==========// @@ -1338,12 +1396,8 @@ void InstrumentEditorFMForm::onLFONumberChanged() { // Change users view - std::vector users = bt_.lock()->getLFOFMUsers(ui->lfoNumSpinBox->value()); - QStringList l; - std::transform(users.begin(), users.end(), std::back_inserter(l), [](int n) { - return QString("%1").arg(n, 2, 16, QChar('0')).toUpper(); - }); - ui->lfoUsersLineEdit->setText(l.join(",")); + std::multiset users = bt_.lock()->getLFOFMUsers(ui->lfoNumSpinBox->value()); + ui->lfoUsersLineEdit->setText(inst_edit_utils::generateUsersString(users)); } void InstrumentEditorFMForm::on_lfoGroupBox_customContextMenuRequested(const QPoint &pos) @@ -1415,14 +1469,13 @@ ui->opSeqNumSpinBox->setValue(instFM->getOperatorSequenceNumber(param)); ui->opSeqEditor->clearData(); setOperatorSequenceEditor(); - for (auto& com : instFM->getOperatorSequenceSequence(param)) { - ui->opSeqEditor->addSequenceCommand(com.type); + for (auto& unit : instFM->getOperatorSequenceSequence(param)) { + ui->opSeqEditor->addSequenceData(unit.data); } - for (auto& l : instFM->getOperatorSequenceLoops(param)) { - ui->opSeqEditor->addLoop(l.begin, l.end, l.times); + for (auto& loop : instFM->getOperatorSequenceLoopRoot(param).getAllLoops()) { + ui->opSeqEditor->addLoop(loop.getBeginPos(), loop.getEndPos(), loop.getTimes()); } - ui->opSeqEditor->setRelease(inst_edit_utils::convertReleaseTypeForUI(instFM->getOperatorSequenceRelease(param).type), - instFM->getOperatorSequenceRelease(param).begin); + ui->opSeqEditor->setRelease(instFM->getOperatorSequenceRelease(param)); if (instFM->getOperatorSequenceEnabled(param)) { ui->opSeqEditGroupBox->setChecked(true); onOperatorSequenceNumberChanged(); @@ -1516,12 +1569,8 @@ void InstrumentEditorFMForm::onOperatorSequenceNumberChanged() { // Change users view - std::vector users = bt_.lock()->getOperatorSequenceFMUsers(getOperatorSequenceParameter(), ui->opSeqNumSpinBox->value()); - QStringList l; - std::transform(users.begin(), users.end(), std::back_inserter(l), [](int n) { - return QString("%1").arg(n, 2, 16, QChar('0')).toUpper(); - }); - ui->opSeqUsersLineEdit->setText(l.join(",")); + std::multiset users = bt_.lock()->getOperatorSequenceFMUsers(getOperatorSequenceParameter(), ui->opSeqNumSpinBox->value()); + ui->opSeqUsersLineEdit->setText(inst_edit_utils::generateUsersString(users)); } void InstrumentEditorFMForm::onOperatorSequenceParameterChanged(FMEnvelopeParameter param, int tnNum) @@ -1580,17 +1629,15 @@ ui->arpNumSpinBox->setValue(instFM->getArpeggioNumber(param)); ui->arpEditor->clearData(); - for (auto& com : instFM->getArpeggioSequence(param)) { - ui->arpEditor->addSequenceCommand(com.type); + for (auto& unit : instFM->getArpeggioSequence(param)) { + ui->arpEditor->addSequenceData(unit.data); } - for (auto& l : instFM->getArpeggioLoops(param)) { - ui->arpEditor->addLoop(l.begin, l.end, l.times); + for (auto& loop : instFM->getArpeggioLoopRoot(param).getAllLoops()) { + ui->arpEditor->addLoop(loop.getBeginPos(), loop.getEndPos(), loop.getTimes()); } - ui->arpEditor->setRelease(inst_edit_utils::convertReleaseTypeForUI(instFM->getArpeggioRelease(param).type), - instFM->getArpeggioRelease(param).begin); + ui->arpEditor->setRelease(instFM->getArpeggioRelease(param)); for (int i = 0; i < ui->arpTypeComboBox->count(); ++i) { - if (instFM->getArpeggioType(param) == inst_edit_utils::convertSequenceTypeForData( - static_cast(ui->arpTypeComboBox->itemData(i).toInt()))) { + if (instFM->getArpeggioType(param) == static_cast(ui->arpTypeComboBox->itemData(i).toInt())) { ui->arpTypeComboBox->setCurrentIndex(i); break; } @@ -1608,12 +1655,8 @@ void InstrumentEditorFMForm::onArpeggioNumberChanged() { // Change users view - std::vector users = bt_.lock()->getArpeggioFMUsers(ui->arpNumSpinBox->value()); - QStringList l; - std::transform(users.begin(), users.end(), std::back_inserter(l), [](int n) { - return QString("%1").arg(n, 2, 16, QChar('0')).toUpper(); - }); - ui->arpUsersLineEdit->setText(l.join(",")); + std::multiset users = bt_.lock()->getArpeggioFMUsers(ui->arpNumSpinBox->value()); + ui->arpUsersLineEdit->setText(inst_edit_utils::generateUsersString(users)); } void InstrumentEditorFMForm::onArpeggioParameterChanged(int tnNum) @@ -1635,10 +1678,9 @@ { Q_UNUSED(index) - auto type = static_cast( - ui->arpTypeComboBox->currentData(Qt::UserRole).toInt()); + auto type = static_cast(ui->arpTypeComboBox->currentData(Qt::UserRole).toInt()); if (!isIgnoreEvent_) { - bt_.lock()->setArpeggioFMType(ui->arpNumSpinBox->value(), inst_edit_utils::convertSequenceTypeForData(type)); + bt_.lock()->setArpeggioFMType(ui->arpNumSpinBox->value(), type); emit arpeggioParameterChanged(ui->arpNumSpinBox->value(), instNum_); emit modified(); } @@ -1687,17 +1729,15 @@ ui->ptNumSpinBox->setValue(instFM->getPitchNumber(param)); ui->ptEditor->clearData(); - for (auto& com : instFM->getPitchSequence(param)) { - ui->ptEditor->addSequenceCommand(com.type); + for (auto& unit : instFM->getPitchSequence(param)) { + ui->ptEditor->addSequenceData(unit.data); } - for (auto& l : instFM->getPitchLoops(param)) { - ui->ptEditor->addLoop(l.begin, l.end, l.times); + for (auto& loop : instFM->getPitchLoopRoot(param).getAllLoops()) { + ui->ptEditor->addLoop(loop.getBeginPos(), loop.getEndPos(), loop.getTimes()); } - ui->ptEditor->setRelease(inst_edit_utils::convertReleaseTypeForUI(instFM->getPitchRelease(param).type), - instFM->getPitchRelease(param).begin); + ui->ptEditor->setRelease(instFM->getPitchRelease(param)); for (int i = 0; i < ui->ptTypeComboBox->count(); ++i) { - if (instFM->getPitchType(param) == inst_edit_utils::convertSequenceTypeForData( - static_cast(ui->ptTypeComboBox->itemData(i).toInt()))) { + if (instFM->getPitchType(param) == static_cast(ui->ptTypeComboBox->itemData(i).toInt())) { ui->ptTypeComboBox->setCurrentIndex(i); break; } @@ -1715,12 +1755,8 @@ void InstrumentEditorFMForm::onPitchNumberChanged() { // Change users view - std::vector users = bt_.lock()->getPitchFMUsers(ui->ptNumSpinBox->value()); - QStringList l; - std::transform(users.begin(), users.end(), std::back_inserter(l), [](int n) { - return QString("%1").arg(n, 2, 16, QChar('0')).toUpper(); - }); - ui->ptUsersLineEdit->setText(l.join(",")); + std::multiset users = bt_.lock()->getPitchFMUsers(ui->ptNumSpinBox->value()); + ui->ptUsersLineEdit->setText(inst_edit_utils::generateUsersString(users)); } void InstrumentEditorFMForm::onPitchParameterChanged(int tnNum) @@ -1742,9 +1778,9 @@ { Q_UNUSED(index) - auto type = static_cast(ui->ptTypeComboBox->currentData(Qt::UserRole).toInt()); + auto type = static_cast(ui->ptTypeComboBox->currentData(Qt::UserRole).toInt()); if (!isIgnoreEvent_) { - bt_.lock()->setPitchFMType(ui->ptNumSpinBox->value(), inst_edit_utils::convertSequenceTypeForData(type)); + bt_.lock()->setPitchFMType(ui->ptNumSpinBox->value(), type); emit pitchParameterChanged(ui->ptNumSpinBox->value(), instNum_); emit modified(); } diff -Nru bambootracker-0.4.5+git20121209/BambooTracker/gui/instrument_editor/instrument_editor_ssg_form.cpp bambootracker-0.4.6/BambooTracker/gui/instrument_editor/instrument_editor_ssg_form.cpp --- bambootracker-0.4.5+git20121209/BambooTracker/gui/instrument_editor/instrument_editor_ssg_form.cpp 2020-12-08 14:05:28.000000000 +0000 +++ bambootracker-0.4.6/BambooTracker/gui/instrument_editor/instrument_editor_ssg_form.cpp 2021-02-11 14:37:17.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (C) 2018-2020 Rerrah + * Copyright (C) 2018-2021 Rerrah * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation @@ -26,16 +26,36 @@ #include "instrument_editor_ssg_form.hpp" #include "ui_instrument_editor_ssg_form.h" #include +#include #include #include #include "gui/event_guard.hpp" -#include "calc_pitch.hpp" -#include "command_sequence.hpp" +#include "note.hpp" +#include "sequence_property.hpp" #include "gui/jam_layout.hpp" #include "gui/instrument_editor/instrument_editor_utils.hpp" -#include "misc.hpp" #include "gui/gui_utils.hpp" +namespace +{ +bool isModulatedWaveformSSG(int type) +{ + switch (type) { + case SSGWaveformType::SQUARE: + case SSGWaveformType::TRIANGLE: + case SSGWaveformType::SAW: + case SSGWaveformType::INVSAW: + return false; + case SSGWaveformType::SQM_TRIANGLE: + case SSGWaveformType::SQM_SAW: + case SSGWaveformType::SQM_INVSAW: + return true; + default: + throw std::invalid_argument("Invalid SSGWaveformType"); + } +} +} + InstrumentEditorSSGForm::InstrumentEditorSSGForm(int num, QWidget *parent) : QWidget(parent), ui(new Ui::InstrumentEditorSSGForm), @@ -55,94 +75,144 @@ ui->waveEditor->AddRow(tr("SMInvSaw"), false); ui->waveEditor->autoFitLabelWidth(); - QObject::connect(ui->waveEditor, &VisualizedInstrumentMacroEditor::sequenceCommandAdded, + QObject::connect(ui->waveEditor, &VisualizedInstrumentMacroEditor::sequenceDataAdded, this, [&](int row, int col) { if (!isIgnoreEvent_) { - if (isModulatedWaveformSSG(row)) setWaveformSequenceColumn(col); // Set square-mask frequency - bt_.lock()->addWaveformSSGSequenceCommand( - ui->waveNumSpinBox->value(), row, ui->waveEditor->getSequenceDataAt(col)); + if (isModulatedWaveformSSG(row)) { + SSGWaveformUnit data = setWaveformSequenceColumn(col, row); // Set square-mask frequency + bt_.lock()->addWaveformSSGSequenceData(ui->waveNumSpinBox->value(), data); + } + else { + bt_.lock()->addWaveformSSGSequenceData(ui->waveNumSpinBox->value(), SSGWaveformUnit::makeOnlyDataUnit(row)); + } emit waveformParameterChanged(ui->waveNumSpinBox->value(), instNum_); emit modified(); } }); - QObject::connect(ui->waveEditor, &VisualizedInstrumentMacroEditor::sequenceCommandRemoved, - this, [&]() { + QObject::connect(ui->waveEditor, &VisualizedInstrumentMacroEditor::sequenceDataRemoved, + this, [&] { if (!isIgnoreEvent_) { - bt_.lock()->removeWaveformSSGSequenceCommand(ui->waveNumSpinBox->value()); + bt_.lock()->removeWaveformSSGSequenceData(ui->waveNumSpinBox->value()); emit waveformParameterChanged(ui->waveNumSpinBox->value(), instNum_); emit modified(); } }); - QObject::connect(ui->waveEditor, &VisualizedInstrumentMacroEditor::sequenceCommandChanged, + QObject::connect(ui->waveEditor, &VisualizedInstrumentMacroEditor::sequenceDataChanged, this, [&](int row, int col) { if (!isIgnoreEvent_) { - if (isModulatedWaveformSSG(row)) setWaveformSequenceColumn(col); // Set square-mask frequency - bt_.lock()->setWaveformSSGSequenceCommand( - ui->waveNumSpinBox->value(), col, row, ui->waveEditor->getSequenceDataAt(col)); + if (isModulatedWaveformSSG(row)) { + SSGWaveformUnit data = setWaveformSequenceColumn(col, row); // Set square-mask frequency + bt_.lock()->setWaveformSSGSequenceData(ui->waveNumSpinBox->value(), col, data); + } + else { + bt_.lock()->setWaveformSSGSequenceData(ui->waveNumSpinBox->value(), col, SSGWaveformUnit::makeOnlyDataUnit(row)); + } + emit waveformParameterChanged(ui->waveNumSpinBox->value(), instNum_); + emit modified(); + } + }); + QObject::connect(ui->waveEditor, &VisualizedInstrumentMacroEditor::loopAdded, + this, [&](InstrumentSequenceLoop loop) { + if (!isIgnoreEvent_) { + bt_.lock()->addWaveformSSGLoop(ui->waveNumSpinBox->value(), loop); + emit waveformParameterChanged(ui->waveNumSpinBox->value(), instNum_); + emit modified(); + } + }); + QObject::connect(ui->waveEditor, &VisualizedInstrumentMacroEditor::loopRemoved, + this, [&](int begin, int end) { + if (!isIgnoreEvent_) { + bt_.lock()->removeWaveformSSGLoop(ui->waveNumSpinBox->value(), begin, end); + emit waveformParameterChanged(ui->waveNumSpinBox->value(), instNum_); + emit modified(); + } + }); + QObject::connect(ui->waveEditor, &VisualizedInstrumentMacroEditor::loopCleared, + this, [&] { + if (!isIgnoreEvent_) { + bt_.lock()->clearWaveformSSGLoops(ui->waveNumSpinBox->value()); emit waveformParameterChanged(ui->waveNumSpinBox->value(), instNum_); emit modified(); } }); QObject::connect(ui->waveEditor, &VisualizedInstrumentMacroEditor::loopChanged, - this, [&](std::vector begins, std::vector ends, std::vector times) { + this, [&](int prevBegin, int prevEnd, InstrumentSequenceLoop loop) { if (!isIgnoreEvent_) { - bt_.lock()->setWaveformSSGLoops( - ui->waveNumSpinBox->value(), std::move(begins), std::move(ends), std::move(times)); + bt_.lock()->changeWaveformSSGLoop(ui->waveNumSpinBox->value(), prevBegin, prevEnd, loop); emit waveformParameterChanged(ui->waveNumSpinBox->value(), instNum_); emit modified(); } }); QObject::connect(ui->waveEditor, &VisualizedInstrumentMacroEditor::releaseChanged, - this, [&](VisualizedInstrumentMacroEditor::ReleaseType type, int point) { + this, [&](InstrumentSequenceRelease release) { if (!isIgnoreEvent_) { - ReleaseType t = inst_edit_utils::convertReleaseTypeForData(type); - bt_.lock()->setWaveformSSGRelease(ui->waveNumSpinBox->value(), t, point); + bt_.lock()->setWaveformSSGRelease(ui->waveNumSpinBox->value(), release); emit waveformParameterChanged(ui->waveNumSpinBox->value(), instNum_); emit modified(); } }); //========== Tone/Noise ==========// - QObject::connect(ui->tnEditor, &VisualizedInstrumentMacroEditor::sequenceCommandAdded, - this, [&](int row, int col) { + QObject::connect(ui->tnEditor, &VisualizedInstrumentMacroEditor::sequenceDataAdded, + this, [&](int row, int) { if (!isIgnoreEvent_) { - bt_.lock()->addToneNoiseSSGSequenceCommand( - ui->tnNumSpinBox->value(), row, ui->tnEditor->getSequenceDataAt(col)); + bt_.lock()->addToneNoiseSSGSequenceData(ui->tnNumSpinBox->value(), row); emit toneNoiseParameterChanged(ui->tnNumSpinBox->value(), instNum_); emit modified(); } }); - QObject::connect(ui->tnEditor, &VisualizedInstrumentMacroEditor::sequenceCommandRemoved, - this, [&]() { + QObject::connect(ui->tnEditor, &VisualizedInstrumentMacroEditor::sequenceDataRemoved, + this, [&] { if (!isIgnoreEvent_) { - bt_.lock()->removeToneNoiseSSGSequenceCommand(ui->tnNumSpinBox->value()); + bt_.lock()->removeToneNoiseSSGSequenceData(ui->tnNumSpinBox->value()); emit toneNoiseParameterChanged(ui->tnNumSpinBox->value(), instNum_); emit modified(); } }); - QObject::connect(ui->tnEditor, &VisualizedInstrumentMacroEditor::sequenceCommandChanged, + QObject::connect(ui->tnEditor, &VisualizedInstrumentMacroEditor::sequenceDataChanged, this, [&](int row, int col) { if (!isIgnoreEvent_) { - bt_.lock()->setToneNoiseSSGSequenceCommand( - ui->tnNumSpinBox->value(), col, row, ui->tnEditor->getSequenceDataAt(col)); + bt_.lock()->setToneNoiseSSGSequenceData(ui->tnNumSpinBox->value(), col, row); + emit toneNoiseParameterChanged(ui->tnNumSpinBox->value(), instNum_); + emit modified(); + } + }); + QObject::connect(ui->tnEditor, &VisualizedInstrumentMacroEditor::loopAdded, + this, [&](InstrumentSequenceLoop loop) { + if (!isIgnoreEvent_) { + bt_.lock()->addToneNoiseSSGLoop(ui->tnNumSpinBox->value(), loop); + emit toneNoiseParameterChanged(ui->tnNumSpinBox->value(), instNum_); + emit modified(); + } + }); + QObject::connect(ui->tnEditor, &VisualizedInstrumentMacroEditor::loopRemoved, + this, [&](int begin, int end) { + if (!isIgnoreEvent_) { + bt_.lock()->removeToneNoiseSSGLoop(ui->tnNumSpinBox->value(), begin, end); + emit toneNoiseParameterChanged(ui->tnNumSpinBox->value(), instNum_); + emit modified(); + } + }); + QObject::connect(ui->tnEditor, &VisualizedInstrumentMacroEditor::loopCleared, + this, [&] { + if (!isIgnoreEvent_) { + bt_.lock()->clearToneNoiseSSGLoops(ui->tnNumSpinBox->value()); emit toneNoiseParameterChanged(ui->tnNumSpinBox->value(), instNum_); emit modified(); } }); QObject::connect(ui->tnEditor, &VisualizedInstrumentMacroEditor::loopChanged, - this, [&](std::vector begins, std::vector ends, std::vector times) { + this, [&](int prevBegin, int prevEnd, InstrumentSequenceLoop loop) { if (!isIgnoreEvent_) { - bt_.lock()->setToneNoiseSSGLoops( - ui->tnNumSpinBox->value(), std::move(begins), std::move(ends), std::move(times)); + bt_.lock()->changeToneNoiseSSGLoop(ui->tnNumSpinBox->value(), prevBegin, prevEnd, loop); emit toneNoiseParameterChanged(ui->tnNumSpinBox->value(), instNum_); emit modified(); } }); QObject::connect(ui->tnEditor, &VisualizedInstrumentMacroEditor::releaseChanged, - this, [&](VisualizedInstrumentMacroEditor::ReleaseType type, int point) { + this, [&](InstrumentSequenceRelease release) { if (!isIgnoreEvent_) { - ReleaseType t = inst_edit_utils::convertReleaseTypeForData(type); - bt_.lock()->setToneNoiseSSGRelease(ui->tnNumSpinBox->value(), t, point); + bt_.lock()->setToneNoiseSSGRelease(ui->tnNumSpinBox->value(), release); emit toneNoiseParameterChanged(ui->tnNumSpinBox->value(), instNum_); emit modified(); } @@ -160,102 +230,152 @@ ui->envEditor->autoFitLabelWidth(); ui->envEditor->setMultipleReleaseState(true); ui->envEditor->setPermittedReleaseTypes( - VisualizedInstrumentMacroEditor::ReleaseType::ABSOLUTE_RELEASE - | VisualizedInstrumentMacroEditor::ReleaseType::RELATIVE_RELEASE - | VisualizedInstrumentMacroEditor::ReleaseType::FIXED_RELEASE); + VisualizedInstrumentMacroEditor::PermittedReleaseFlag::ABSOLUTE_RELEASE + | VisualizedInstrumentMacroEditor::PermittedReleaseFlag::RELATIVE_RELEASE + | VisualizedInstrumentMacroEditor::PermittedReleaseFlag::FIXED_RELEASE); - QObject::connect(ui->envEditor, &VisualizedInstrumentMacroEditor::sequenceCommandAdded, + QObject::connect(ui->envEditor, &VisualizedInstrumentMacroEditor::sequenceDataAdded, this, [&](int row, int col) { if (!isIgnoreEvent_) { - if (row >= 16) setEnvelopeSequenceColumn(col); // Set hard frequency - bt_.lock()->addEnvelopeSSGSequenceCommand( - ui->envNumSpinBox->value(), row, ui->envEditor->getSequenceDataAt(col)); + if (row >= 16) { + SSGEnvelopeUnit data = setEnvelopeSequenceColumn(col, row); // Set hard frequency + bt_.lock()->addEnvelopeSSGSequenceData(ui->envNumSpinBox->value(), data); + } + else { + bt_.lock()->addEnvelopeSSGSequenceData(ui->envNumSpinBox->value(), SSGEnvelopeUnit::makeOnlyDataUnit(row)); + } emit envelopeParameterChanged(ui->envNumSpinBox->value(), instNum_); emit modified(); } }); - QObject::connect(ui->envEditor, &VisualizedInstrumentMacroEditor::sequenceCommandRemoved, - this, [&]() { + QObject::connect(ui->envEditor, &VisualizedInstrumentMacroEditor::sequenceDataRemoved, + this, [&] { if (!isIgnoreEvent_) { - bt_.lock()->removeEnvelopeSSGSequenceCommand(ui->envNumSpinBox->value()); + bt_.lock()->removeEnvelopeSSGSequenceData(ui->envNumSpinBox->value()); emit envelopeParameterChanged(ui->envNumSpinBox->value(), instNum_); emit modified(); } }); - QObject::connect(ui->envEditor, &VisualizedInstrumentMacroEditor::sequenceCommandChanged, + QObject::connect(ui->envEditor, &VisualizedInstrumentMacroEditor::sequenceDataChanged, this, [&](int row, int col) { if (!isIgnoreEvent_) { - if (row >= 16) setEnvelopeSequenceColumn(col); // Set hard frequency - bt_.lock()->setEnvelopeSSGSequenceCommand( - ui->envNumSpinBox->value(), col, row, ui->envEditor->getSequenceDataAt(col)); + if (row >= 16) { + SSGEnvelopeUnit data = setEnvelopeSequenceColumn(col, row); // Set hard frequency + bt_.lock()->setEnvelopeSSGSequenceData(ui->envNumSpinBox->value(), col, data); + } + else { + bt_.lock()->setEnvelopeSSGSequenceData(ui->envNumSpinBox->value(), col, SSGEnvelopeUnit::makeOnlyDataUnit(row)); + } + emit envelopeParameterChanged(ui->envNumSpinBox->value(), instNum_); + emit modified(); + } + }); + QObject::connect(ui->envEditor, &VisualizedInstrumentMacroEditor::loopAdded, + this, [&](InstrumentSequenceLoop loop) { + if (!isIgnoreEvent_) { + bt_.lock()->addEnvelopeSSGLoop(ui->envNumSpinBox->value(), loop); + emit envelopeParameterChanged(ui->envNumSpinBox->value(), instNum_); + emit modified(); + } + }); + QObject::connect(ui->envEditor, &VisualizedInstrumentMacroEditor::loopRemoved, + this, [&](int begin, int end) { + if (!isIgnoreEvent_) { + bt_.lock()->removeEnvelopeSSGLoop(ui->envNumSpinBox->value(), begin, end); + emit envelopeParameterChanged(ui->envNumSpinBox->value(), instNum_); + emit modified(); + } + }); + QObject::connect(ui->envEditor, &VisualizedInstrumentMacroEditor::loopCleared, + this, [&] { + if (!isIgnoreEvent_) { + bt_.lock()->clearEnvelopeSSGLoops(ui->envNumSpinBox->value()); emit envelopeParameterChanged(ui->envNumSpinBox->value(), instNum_); emit modified(); } }); QObject::connect(ui->envEditor, &VisualizedInstrumentMacroEditor::loopChanged, - this, [&](std::vector begins, std::vector ends, std::vector times) { + this, [&](int prevBegin, int prevEnd, InstrumentSequenceLoop loop) { if (!isIgnoreEvent_) { - bt_.lock()->setEnvelopeSSGLoops( - ui->envNumSpinBox->value(), std::move(begins), std::move(ends), std::move(times)); + bt_.lock()->changeEnvelopeSSGLoop(ui->envNumSpinBox->value(), prevBegin, prevEnd, loop); emit envelopeParameterChanged(ui->envNumSpinBox->value(), instNum_); emit modified(); } }); QObject::connect(ui->envEditor, &VisualizedInstrumentMacroEditor::releaseChanged, - this, [&](VisualizedInstrumentMacroEditor::ReleaseType type, int point) { + this, [&](InstrumentSequenceRelease release) { if (!isIgnoreEvent_) { - ReleaseType t = inst_edit_utils::convertReleaseTypeForData(type); - bt_.lock()->setEnvelopeSSGRelease(ui->envNumSpinBox->value(), t, point); + bt_.lock()->setEnvelopeSSGRelease(ui->envNumSpinBox->value(), release); emit envelopeParameterChanged(ui->envNumSpinBox->value(), instNum_); emit modified(); } }); //========== Arpeggio ==========// - ui->arpTypeComboBox->addItem(tr("Absolute"), VisualizedInstrumentMacroEditor::SequenceType::AbsoluteSequence); - ui->arpTypeComboBox->addItem(tr("Fixed"), VisualizedInstrumentMacroEditor::SequenceType::FixedSequence); - ui->arpTypeComboBox->addItem(tr("Relative"), VisualizedInstrumentMacroEditor::SequenceType::RelativeSequence); + ui->arpTypeComboBox->addItem(tr("Absolute"), static_cast(SequenceType::AbsoluteSequence)); + ui->arpTypeComboBox->addItem(tr("Fixed"), static_cast(SequenceType::FixedSequence)); + ui->arpTypeComboBox->addItem(tr("Relative"), static_cast(SequenceType::RelativeSequence)); - QObject::connect(ui->arpEditor, &VisualizedInstrumentMacroEditor::sequenceCommandAdded, - this, [&](int row, int col) { + QObject::connect(ui->arpEditor, &VisualizedInstrumentMacroEditor::sequenceDataAdded, + this, [&](int row, int) { if (!isIgnoreEvent_) { - bt_.lock()->addArpeggioSSGSequenceCommand( - ui->arpNumSpinBox->value(), row, ui->arpEditor->getSequenceDataAt(col)); + bt_.lock()->addArpeggioSSGSequenceData(ui->arpNumSpinBox->value(), row); emit arpeggioParameterChanged(ui->arpNumSpinBox->value(), instNum_); emit modified(); } }); - QObject::connect(ui->arpEditor, &VisualizedInstrumentMacroEditor::sequenceCommandRemoved, - this, [&]() { + QObject::connect(ui->arpEditor, &VisualizedInstrumentMacroEditor::sequenceDataRemoved, + this, [&] { if (!isIgnoreEvent_) { - bt_.lock()->removeArpeggioSSGSequenceCommand(ui->arpNumSpinBox->value()); + bt_.lock()->removeArpeggioSSGSequenceData(ui->arpNumSpinBox->value()); emit arpeggioParameterChanged(ui->arpNumSpinBox->value(), instNum_); emit modified(); } }); - QObject::connect(ui->arpEditor, &VisualizedInstrumentMacroEditor::sequenceCommandChanged, + QObject::connect(ui->arpEditor, &VisualizedInstrumentMacroEditor::sequenceDataChanged, this, [&](int row, int col) { if (!isIgnoreEvent_) { - bt_.lock()->setArpeggioSSGSequenceCommand( - ui->arpNumSpinBox->value(), col, row, ui->arpEditor->getSequenceDataAt(col)); + bt_.lock()->setArpeggioSSGSequenceData(ui->arpNumSpinBox->value(), col, row); + emit arpeggioParameterChanged(ui->arpNumSpinBox->value(), instNum_); + emit modified(); + } + }); + QObject::connect(ui->arpEditor, &VisualizedInstrumentMacroEditor::loopAdded, + this, [&](InstrumentSequenceLoop loop) { + if (!isIgnoreEvent_) { + bt_.lock()->addArpeggioSSGLoop(ui->arpNumSpinBox->value(), loop); + emit arpeggioParameterChanged(ui->arpNumSpinBox->value(), instNum_); + emit modified(); + } + }); + QObject::connect(ui->arpEditor, &VisualizedInstrumentMacroEditor::loopRemoved, + this, [&](int begin, int end) { + if (!isIgnoreEvent_) { + bt_.lock()->removeArpeggioSSGLoop(ui->arpNumSpinBox->value(), begin, end); + emit arpeggioParameterChanged(ui->arpNumSpinBox->value(), instNum_); + emit modified(); + } + }); + QObject::connect(ui->arpEditor, &VisualizedInstrumentMacroEditor::loopCleared, + this, [&] { + if (!isIgnoreEvent_) { + bt_.lock()->clearArpeggioSSGLoops(ui->arpNumSpinBox->value()); emit arpeggioParameterChanged(ui->arpNumSpinBox->value(), instNum_); emit modified(); } }); QObject::connect(ui->arpEditor, &VisualizedInstrumentMacroEditor::loopChanged, - this, [&](std::vector begins, std::vector ends, std::vector times) { + this, [&](int prevBegin, int prevEnd, InstrumentSequenceLoop loop) { if (!isIgnoreEvent_) { - bt_.lock()->setArpeggioSSGLoops( - ui->arpNumSpinBox->value(), std::move(begins), std::move(ends), std::move(times)); + bt_.lock()->changeArpeggioSSGLoop(ui->arpNumSpinBox->value(), prevBegin, prevEnd, loop); emit arpeggioParameterChanged(ui->arpNumSpinBox->value(), instNum_); emit modified(); } }); QObject::connect(ui->arpEditor, &VisualizedInstrumentMacroEditor::releaseChanged, - this, [&](VisualizedInstrumentMacroEditor::ReleaseType type, int point) { + this, [&](InstrumentSequenceRelease release) { if (!isIgnoreEvent_) { - ReleaseType t = inst_edit_utils::convertReleaseTypeForData(type); - bt_.lock()->setArpeggioSSGRelease(ui->arpNumSpinBox->value(), t, point); + bt_.lock()->setArpeggioSSGRelease(ui->arpNumSpinBox->value(), release); emit arpeggioParameterChanged(ui->arpNumSpinBox->value(), instNum_); emit modified(); } @@ -275,49 +395,69 @@ ui->ptEditor->setUpperRow(134); ui->ptEditor->setMMLDisplay0As(-127); - ui->ptTypeComboBox->addItem(tr("Absolute"), VisualizedInstrumentMacroEditor::SequenceType::AbsoluteSequence); - ui->ptTypeComboBox->addItem(tr("Relative"), VisualizedInstrumentMacroEditor::SequenceType::RelativeSequence); + ui->ptTypeComboBox->addItem(tr("Absolute"), static_cast(SequenceType::AbsoluteSequence)); + ui->ptTypeComboBox->addItem(tr("Relative"), static_cast(SequenceType::RelativeSequence)); - QObject::connect(ui->ptEditor, &VisualizedInstrumentMacroEditor::sequenceCommandAdded, - this, [&](int row, int col) { + QObject::connect(ui->ptEditor, &VisualizedInstrumentMacroEditor::sequenceDataAdded, + this, [&](int row, int) { if (!isIgnoreEvent_) { - bt_.lock()->addPitchSSGSequenceCommand( - ui->ptNumSpinBox->value(), row, ui->ptEditor->getSequenceDataAt(col)); + bt_.lock()->addPitchSSGSequenceData(ui->ptNumSpinBox->value(), row); emit pitchParameterChanged(ui->ptNumSpinBox->value(), instNum_); emit modified(); } }); - QObject::connect(ui->ptEditor, &VisualizedInstrumentMacroEditor::sequenceCommandRemoved, - this, [&]() { + QObject::connect(ui->ptEditor, &VisualizedInstrumentMacroEditor::sequenceDataRemoved, + this, [&] { if (!isIgnoreEvent_) { - bt_.lock()->removePitchSSGSequenceCommand(ui->ptNumSpinBox->value()); + bt_.lock()->removePitchSSGSequenceData(ui->ptNumSpinBox->value()); emit pitchParameterChanged(ui->ptNumSpinBox->value(), instNum_); emit modified(); } }); - QObject::connect(ui->ptEditor, &VisualizedInstrumentMacroEditor::sequenceCommandChanged, + QObject::connect(ui->ptEditor, &VisualizedInstrumentMacroEditor::sequenceDataChanged, this, [&](int row, int col) { if (!isIgnoreEvent_) { - bt_.lock()->setPitchSSGSequenceCommand( - ui->ptNumSpinBox->value(), col, row, ui->ptEditor->getSequenceDataAt(col)); + bt_.lock()->setPitchSSGSequenceData(ui->ptNumSpinBox->value(), col, row); + emit pitchParameterChanged(ui->ptNumSpinBox->value(), instNum_); + emit modified(); + } + }); + QObject::connect(ui->ptEditor, &VisualizedInstrumentMacroEditor::loopAdded, + this, [&](InstrumentSequenceLoop loop) { + if (!isIgnoreEvent_) { + bt_.lock()->addPitchSSGLoop(ui->ptNumSpinBox->value(), loop); + emit pitchParameterChanged(ui->ptNumSpinBox->value(), instNum_); + emit modified(); + } + }); + QObject::connect(ui->ptEditor, &VisualizedInstrumentMacroEditor::loopRemoved, + this, [&](int begin, int end) { + if (!isIgnoreEvent_) { + bt_.lock()->removePitchSSGLoop(ui->ptNumSpinBox->value(), begin, end); + emit pitchParameterChanged(ui->ptNumSpinBox->value(), instNum_); + emit modified(); + } + }); + QObject::connect(ui->ptEditor, &VisualizedInstrumentMacroEditor::loopCleared, + this, [&] { + if (!isIgnoreEvent_) { + bt_.lock()->clearPitchSSGLoops(ui->ptNumSpinBox->value()); emit pitchParameterChanged(ui->ptNumSpinBox->value(), instNum_); emit modified(); } }); QObject::connect(ui->ptEditor, &VisualizedInstrumentMacroEditor::loopChanged, - this, [&](std::vector begins, std::vector ends, std::vector times) { + this, [&](int prevBegin, int prevEnd, InstrumentSequenceLoop loop) { if (!isIgnoreEvent_) { - bt_.lock()->setPitchSSGLoops( - ui->ptNumSpinBox->value(), std::move(begins), std::move(ends), std::move(times)); + bt_.lock()->changePitchSSGLoop(ui->ptNumSpinBox->value(), prevBegin, prevEnd, loop); emit pitchParameterChanged(ui->ptNumSpinBox->value(), instNum_); emit modified(); } }); QObject::connect(ui->ptEditor, &VisualizedInstrumentMacroEditor::releaseChanged, - this, [&](VisualizedInstrumentMacroEditor::ReleaseType type, int point) { + this, [&](InstrumentSequenceRelease release) { if (!isIgnoreEvent_) { - ReleaseType t = inst_edit_utils::convertReleaseTypeForData(type); - bt_.lock()->setPitchSSGRelease(ui->ptNumSpinBox->value(), t, point); + bt_.lock()->setPitchSSGRelease(ui->ptNumSpinBox->value(), release); emit pitchParameterChanged(ui->ptNumSpinBox->value(), instNum_); emit modified(); } @@ -422,24 +562,24 @@ ui->waveNumSpinBox->setValue(instSSG->getWaveformNumber()); ui->waveEditor->clearData(); - for (auto& com : instSSG->getWaveformSequence()) { + for (auto& unit : instSSG->getWaveformSequence()) { QString str(""); - if (isModulatedWaveformSSG(com.type)) { - if (CommandSequenceUnit::checkDataType(com.data) == CommandSequenceUnit::RATIO) { - auto ratio = CommandSequenceUnit::data2ratio(com.data); - str = QString("%1/%2").arg(ratio.first).arg(ratio.second); + if (isModulatedWaveformSSG(unit.data)) { + if (unit.type == SSGWaveformUnit::RatioSubdata) { + int r1, r2; + unit.getSubdataAsRatio(r1, r2); + str = QString("%1/%2").arg(r1).arg(r2); } else { - str = QString::number(com.data); + str = QString::number(unit.subdata); } } - ui->waveEditor->addSequenceCommand(com.type, str, com.data); + ui->waveEditor->addSequenceData(unit.data, str, unit.subdata); } - for (auto& l : instSSG->getWaveformLoops()) { - ui->waveEditor->addLoop(l.begin, l.end, l.times); + for (auto& loop : instSSG->getWaveformLoopRoot().getAllLoops()) { + ui->waveEditor->addLoop(loop.getBeginPos(), loop.getEndPos(), loop.getTimes()); } - ui->waveEditor->setRelease(inst_edit_utils::convertReleaseTypeForUI(instSSG->getWaveformRelease().type), - instSSG->getWaveformRelease().begin); + ui->waveEditor->setRelease(instSSG->getWaveformRelease()); if (instSSG->getWaveformEnabled()) { ui->waveEditGroupBox->setChecked(true); onWaveformNumberChanged(); @@ -449,20 +589,22 @@ } } -void InstrumentEditorSSGForm::setWaveformSequenceColumn(int col) +SSGWaveformUnit InstrumentEditorSSGForm::setWaveformSequenceColumn(int col, int wfRow) { auto button = ui->squareMaskButtonGroup->checkedButton(); if (button == ui->squareMaskRawRadioButton) { - ui->waveEditor->setText(col, QString::number(ui->squareMaskRawSpinBox->value())); - ui->waveEditor->setData(col, ui->squareMaskRawSpinBox->value()); + SSGWaveformUnit unit = SSGWaveformUnit::makeRawUnit(wfRow, ui->squareMaskRawSpinBox->value()); + ui->waveEditor->setText(col, QString::number(unit.subdata)); + ui->waveEditor->setSubdata(col, unit.subdata); + return unit; } else { + SSGWaveformUnit unit = SSGWaveformUnit::makeRatioUnit(wfRow, ui->squareMaskToneSpinBox->value(), + ui->squareMaskMaskSpinBox->value()); ui->waveEditor->setText(col, QString::number(ui->squareMaskToneSpinBox->value()) + "/" + QString::number(ui->squareMaskMaskSpinBox->value())); - - ui->waveEditor->setData(col, CommandSequenceUnit::ratio2data( - ui->squareMaskToneSpinBox->value(), - ui->squareMaskMaskSpinBox->value())); + ui->waveEditor->setSubdata(col, unit.subdata); + return unit; } } @@ -470,12 +612,8 @@ void InstrumentEditorSSGForm::onWaveformNumberChanged() { // Change users view - std::vector users = bt_.lock()->getWaveformSSGUsers(ui->waveNumSpinBox->value()); - QStringList l; - std::transform(users.begin(), users.end(), std::back_inserter(l), [](int n) { - return QString("%1").arg(n, 2, 16, QChar('0')).toUpper(); - }); - ui->waveUsersLineEdit->setText(l.join(",")); + std::multiset users = bt_.lock()->getWaveformSSGUsers(ui->waveNumSpinBox->value()); + ui->waveUsersLineEdit->setText(inst_edit_utils::generateUsersString(users)); } void InstrumentEditorSSGForm::onWaveformParameterChanged(int wfNum) @@ -528,14 +666,13 @@ ui->tnNumSpinBox->setValue(instSSG->getToneNoiseNumber()); ui->tnEditor->clearData(); - for (auto& com : instSSG->getToneNoiseSequence()) { - ui->tnEditor->addSequenceCommand(com.type); + for (auto& unit : instSSG->getToneNoiseSequence()) { + ui->tnEditor->addSequenceData(unit.data); } - for (auto& l : instSSG->getToneNoiseLoops()) { - ui->tnEditor->addLoop(l.begin, l.end, l.times); + for (auto& loop : instSSG->getToneNoiseLoopRoot().getAllLoops()) { + ui->tnEditor->addLoop(loop.getBeginPos(), loop.getEndPos(), loop.getTimes()); } - ui->tnEditor->setRelease(inst_edit_utils::convertReleaseTypeForUI(instSSG->getToneNoiseRelease().type), - instSSG->getToneNoiseRelease().begin); + ui->tnEditor->setRelease(instSSG->getToneNoiseRelease()); if (instSSG->getToneNoiseEnabled()) { ui->tnEditGroupBox->setChecked(true); onToneNoiseNumberChanged(); @@ -549,12 +686,8 @@ void InstrumentEditorSSGForm::onToneNoiseNumberChanged() { // Change users view - std::vector users = bt_.lock()->getToneNoiseSSGUsers(ui->tnNumSpinBox->value()); - QStringList l; - std::transform(users.begin(), users.end(), std::back_inserter(l), [](int n) { - return QString("%1").arg(n, 2, 16, QChar('0')).toUpper(); - }); - ui->tnUsersLineEdit->setText(l.join((","))); + std::multiset users = bt_.lock()->getToneNoiseSSGUsers(ui->tnNumSpinBox->value()); + ui->tnUsersLineEdit->setText(inst_edit_utils::generateUsersString(users)); } void InstrumentEditorSSGForm::onToneNoiseParameterChanged(int tnNum) @@ -599,24 +732,24 @@ ui->envNumSpinBox->setValue(instSSG->getEnvelopeNumber()); ui->envEditor->clearData(); - for (auto& com : instSSG->getEnvelopeSequence()) { + for (auto& unit : instSSG->getEnvelopeSequence()) { QString str(""); - if (com.type >= 16) { - if (CommandSequenceUnit::checkDataType(com.data) == CommandSequenceUnit::RATIO) { - auto ratio = CommandSequenceUnit::data2ratio(com.data); - str = QString("%1/%2").arg(ratio.first).arg(ratio.second); + if (unit.data >= 16) { + if (unit.type == SSGEnvelopeUnit::RatioSubdata) { + int r1, r2; + unit.getSubdataAsRatio(r1, r2); + str = QString("%1/%2").arg(r1).arg(r2); } else { - str = QString::number(com.data); + str = QString::number(unit.subdata); } } - ui->envEditor->addSequenceCommand(com.type, str, com.data); + ui->envEditor->addSequenceData(unit.data, str, unit.subdata); } - for (auto& l : instSSG->getEnvelopeLoops()) { - ui->envEditor->addLoop(l.begin, l.end, l.times); + for (auto& loop : instSSG->getEnvelopeLoopRoot().getAllLoops()) { + ui->envEditor->addLoop(loop.getBeginPos(), loop.getEndPos(), loop.getTimes()); } - ui->envEditor->setRelease(inst_edit_utils::convertReleaseTypeForUI(instSSG->getEnvelopeRelease().type), - instSSG->getEnvelopeRelease().begin); + ui->envEditor->setRelease(instSSG->getEnvelopeRelease()); if (instSSG->getEnvelopeEnabled()) { ui->envEditGroupBox->setChecked(true); onEnvelopeNumberChanged(); @@ -626,19 +759,21 @@ } } -void InstrumentEditorSSGForm::setEnvelopeSequenceColumn(int col) +SSGEnvelopeUnit InstrumentEditorSSGForm::setEnvelopeSequenceColumn(int col, int envRow) { if (ui->hardFreqButtonGroup->checkedButton() == ui->hardFreqRawRadioButton) { - ui->envEditor->setText(col, QString::number(ui->hardFreqRawSpinBox->value())); - ui->envEditor->setData(col, ui->hardFreqRawSpinBox->value()); + SSGEnvelopeUnit unit = SSGEnvelopeUnit::makeRawUnit(envRow, ui->hardFreqRawSpinBox->value()); + ui->envEditor->setText(col, QString::number(unit.subdata)); + ui->envEditor->setSubdata(col, unit.subdata); + return unit; } else { + SSGEnvelopeUnit unit = SSGEnvelopeUnit::makeRatioUnit(envRow, ui->hardFreqToneSpinBox->value(), + ui->hardFreqHardSpinBox->value()); ui->envEditor->setText(col, QString::number(ui->hardFreqToneSpinBox->value()) + "/" + QString::number(ui->hardFreqHardSpinBox->value())); - - ui->envEditor->setData(col, CommandSequenceUnit::ratio2data( - ui->hardFreqToneSpinBox->value(), - ui->hardFreqHardSpinBox->value())); + ui->envEditor->setSubdata(col, unit.subdata); + return unit; } } @@ -646,12 +781,8 @@ void InstrumentEditorSSGForm::onEnvelopeNumberChanged() { // Change users view - std::vector users = bt_.lock()->getEnvelopeSSGUsers(ui->envNumSpinBox->value()); - QStringList l; - std::transform(users.begin(), users.end(), std::back_inserter(l), [](int n) { - return QString("%1").arg(n, 2, 16, QChar('0')).toUpper(); - }); - ui->envUsersLineEdit->setText(l.join((","))); + std::multiset users = bt_.lock()->getEnvelopeSSGUsers(ui->envNumSpinBox->value()); + ui->envUsersLineEdit->setText(inst_edit_utils::generateUsersString(users)); } void InstrumentEditorSSGForm::onEnvelopeParameterChanged(int envNum) @@ -703,17 +834,15 @@ ui->arpNumSpinBox->setValue(instSSG->getArpeggioNumber()); ui->arpEditor->clearData(); - for (auto& com : instSSG->getArpeggioSequence()) { - ui->arpEditor->addSequenceCommand(com.type); + for (auto& unit : instSSG->getArpeggioSequence()) { + ui->arpEditor->addSequenceData(unit.data); } - for (auto& l : instSSG->getArpeggioLoops()) { - ui->arpEditor->addLoop(l.begin, l.end, l.times); + for (auto& loop : instSSG->getArpeggioLoopRoot().getAllLoops()) { + ui->arpEditor->addLoop(loop.getBeginPos(), loop.getEndPos(), loop.getTimes()); } - ui->arpEditor->setRelease(inst_edit_utils::convertReleaseTypeForUI(instSSG->getArpeggioRelease().type), - instSSG->getArpeggioRelease().begin); + ui->arpEditor->setRelease(instSSG->getArpeggioRelease()); for (int i = 0; i < ui->arpTypeComboBox->count(); ++i) { - if (instSSG->getArpeggioType() == inst_edit_utils::convertSequenceTypeForData( - static_cast(ui->arpTypeComboBox->itemData(i).toInt()))) { + if (instSSG->getArpeggioType() == static_cast(ui->arpTypeComboBox->itemData(i).toInt())) { ui->arpTypeComboBox->setCurrentIndex(i); break; } @@ -731,12 +860,8 @@ void InstrumentEditorSSGForm::onArpeggioNumberChanged() { // Change users view - std::vector users = bt_.lock()->getArpeggioSSGUsers(ui->arpNumSpinBox->value()); - QStringList l; - std::transform(users.begin(), users.end(), std::back_inserter(l), [](int n) { - return QString("%1").arg(n, 2, 16, QChar('0')).toUpper(); - }); - ui->arpUsersLineEdit->setText(l.join(",")); + std::multiset users = bt_.lock()->getArpeggioSSGUsers(ui->arpNumSpinBox->value()); + ui->arpUsersLineEdit->setText(inst_edit_utils::generateUsersString(users)); } void InstrumentEditorSSGForm::onArpeggioParameterChanged(int tnNum) @@ -751,10 +876,9 @@ { Q_UNUSED(index) - auto type = static_cast( - ui->arpTypeComboBox->currentData(Qt::UserRole).toInt()); + auto type = static_cast(ui->arpTypeComboBox->currentData(Qt::UserRole).toInt()); if (!isIgnoreEvent_) { - bt_.lock()->setArpeggioSSGType(ui->arpNumSpinBox->value(), inst_edit_utils::convertSequenceTypeForData(type)); + bt_.lock()->setArpeggioSSGType(ui->arpNumSpinBox->value(), type); emit arpeggioParameterChanged(ui->arpNumSpinBox->value(), instNum_); emit modified(); } @@ -796,17 +920,15 @@ ui->ptNumSpinBox->setValue(instSSG->getPitchNumber()); ui->ptEditor->clearData(); - for (auto& com : instSSG->getPitchSequence()) { - ui->ptEditor->addSequenceCommand(com.type); + for (auto& unit : instSSG->getPitchSequence()) { + ui->ptEditor->addSequenceData(unit.data); } - for (auto& l : instSSG->getPitchLoops()) { - ui->ptEditor->addLoop(l.begin, l.end, l.times); + for (auto& loop : instSSG->getPitchLoopRoot().getAllLoops()) { + ui->ptEditor->addLoop(loop.getBeginPos(), loop.getEndPos(), loop.getTimes()); } - ui->ptEditor->setRelease(inst_edit_utils::convertReleaseTypeForUI(instSSG->getPitchRelease().type), - instSSG->getPitchRelease().begin); + ui->ptEditor->setRelease(instSSG->getPitchRelease()); for (int i = 0; i < ui->ptTypeComboBox->count(); ++i) { - if (instSSG->getPitchType() == inst_edit_utils::convertSequenceTypeForData( - static_cast(ui->ptTypeComboBox->itemData(i).toInt()))) { + if (instSSG->getPitchType() == static_cast(ui->ptTypeComboBox->itemData(i).toInt())) { ui->ptTypeComboBox->setCurrentIndex(i); break; } @@ -824,12 +946,8 @@ void InstrumentEditorSSGForm::onPitchNumberChanged() { // Change users view - std::vector users = bt_.lock()->getPitchSSGUsers(ui->ptNumSpinBox->value()); - QStringList l; - std::transform(users.begin(), users.end(), std::back_inserter(l), [](int n) { - return QString("%1").arg(n, 2, 16, QChar('0')).toUpper(); - }); - ui->ptUsersLineEdit->setText(l.join(",")); + std::multiset users = bt_.lock()->getPitchSSGUsers(ui->ptNumSpinBox->value()); + ui->ptUsersLineEdit->setText(inst_edit_utils::generateUsersString(users)); } void InstrumentEditorSSGForm::onPitchParameterChanged(int tnNum) @@ -844,9 +962,9 @@ { Q_UNUSED(index) - auto type = static_cast(ui->ptTypeComboBox->currentData(Qt::UserRole).toInt()); + auto type = static_cast(ui->ptTypeComboBox->currentData(Qt::UserRole).toInt()); if (!isIgnoreEvent_) { - bt_.lock()->setPitchSSGType(ui->ptNumSpinBox->value(), inst_edit_utils::convertSequenceTypeForData(type)); + bt_.lock()->setPitchSSGType(ui->ptNumSpinBox->value(), type); emit pitchParameterChanged(ui->ptNumSpinBox->value(), instNum_); emit modified(); } diff -Nru bambootracker-0.4.5+git20121209/BambooTracker/gui/instrument_editor/instrument_editor_ssg_form.hpp bambootracker-0.4.6/BambooTracker/gui/instrument_editor/instrument_editor_ssg_form.hpp --- bambootracker-0.4.5+git20121209/BambooTracker/gui/instrument_editor/instrument_editor_ssg_form.hpp 2020-12-08 14:05:28.000000000 +0000 +++ bambootracker-0.4.6/BambooTracker/gui/instrument_editor/instrument_editor_ssg_form.hpp 2021-02-11 14:37:17.000000000 +0000 @@ -33,6 +33,7 @@ #include "instrument.hpp" #include "configuration.hpp" #include "jamming.hpp" +#include "instrument/instrument_property_defs.hpp" #include "gui/instrument_editor/visualized_instrument_macro_editor.hpp" #include "gui/color_palette.hpp" @@ -83,7 +84,7 @@ private: void setInstrumentWaveformParameters(); - void setWaveformSequenceColumn(int col); + SSGWaveformUnit setWaveformSequenceColumn(int col, int wfRow); private slots: void on_waveEditGroupBox_toggled(bool arg1); @@ -117,7 +118,7 @@ private: void setInstrumentEnvelopeParameters(); - void setEnvelopeSequenceColumn(int col); + SSGEnvelopeUnit setEnvelopeSequenceColumn(int col, int envRow); private slots: void on_envEditGroupBox_toggled(bool arg1); diff -Nru bambootracker-0.4.5+git20121209/BambooTracker/gui/instrument_editor/instrument_editor_utils.cpp bambootracker-0.4.6/BambooTracker/gui/instrument_editor/instrument_editor_utils.cpp --- bambootracker-0.4.5+git20121209/BambooTracker/gui/instrument_editor/instrument_editor_utils.cpp 2020-12-08 14:05:28.000000000 +0000 +++ bambootracker-0.4.6/BambooTracker/gui/instrument_editor/instrument_editor_utils.cpp 2021-02-11 14:37:17.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (C) 2020 Rerrah + * Copyright (C) 2020-2021 Rerrah * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation @@ -24,50 +24,17 @@ */ #include "instrument_editor_utils.hpp" -#include #include -#include "enum_hash.hpp" - -namespace -{ -const std::unordered_map SEQ_TYPE_MAP = { - { VisualizedInstrumentMacroEditor::SequenceType::NoType, SequenceType::NO_SEQUENCE_TYPE }, - { VisualizedInstrumentMacroEditor::SequenceType::FixedSequence, SequenceType::FIXED_SEQUENCE }, - { VisualizedInstrumentMacroEditor::SequenceType::AbsoluteSequence, SequenceType::ABSOLUTE_SEQUENCE }, - { VisualizedInstrumentMacroEditor::SequenceType::RelativeSequence, SequenceType::RELATIVE_SEQUENCE } -}; - -const std::unordered_map REL_TYPE_MAP = { - { VisualizedInstrumentMacroEditor::ReleaseType::NO_RELEASE, ReleaseType::NoRelease }, - { VisualizedInstrumentMacroEditor::ReleaseType::FIXED_RELEASE, ReleaseType::FixedRelease }, - { VisualizedInstrumentMacroEditor::ReleaseType::ABSOLUTE_RELEASE, ReleaseType::AbsoluteRelease }, - { VisualizedInstrumentMacroEditor::ReleaseType::RELATIVE_RELEASE, ReleaseType::RelativeRelease } -}; -} +#include namespace inst_edit_utils { -SequenceType convertSequenceTypeForData(VisualizedInstrumentMacroEditor::SequenceType type) -{ - return SEQ_TYPE_MAP.at(type); -} - -VisualizedInstrumentMacroEditor::SequenceType convertSequenceTypeForUI(SequenceType type) -{ - return std::find_if(SEQ_TYPE_MAP.begin(), SEQ_TYPE_MAP.end(), [type](const auto& pair) { - return (pair.second == type); - })->first; -} - -ReleaseType convertReleaseTypeForData(VisualizedInstrumentMacroEditor::ReleaseType type) -{ - return REL_TYPE_MAP.at(type); -} - -VisualizedInstrumentMacroEditor::ReleaseType convertReleaseTypeForUI(ReleaseType type) +QString generateUsersString(const std::multiset& users) { - return std::find_if(REL_TYPE_MAP.begin(), REL_TYPE_MAP.end(), [type](const auto& pair) { - return (pair.second == type); - })->first; + QStringList l; + std::transform(users.begin(), users.end(), std::back_inserter(l), [](int n) { + return QString("%1").arg(n, 2, 16, QChar('0')); + }); + return l.join(",").toUpper(); } } diff -Nru bambootracker-0.4.5+git20121209/BambooTracker/gui/instrument_editor/instrument_editor_utils.hpp bambootracker-0.4.6/BambooTracker/gui/instrument_editor/instrument_editor_utils.hpp --- bambootracker-0.4.5+git20121209/BambooTracker/gui/instrument_editor/instrument_editor_utils.hpp 2020-12-08 14:05:28.000000000 +0000 +++ bambootracker-0.4.6/BambooTracker/gui/instrument_editor/instrument_editor_utils.hpp 2021-02-11 14:37:17.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (C) 2020 Rerrah + * Copyright (C) 2020-2021 Rerrah * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation @@ -26,15 +26,12 @@ #ifndef INSTRUMENT_EDITOR_UTILS_HPP #define INSTRUMENT_EDITOR_UTILS_HPP -#include "command_sequence.hpp" -#include "gui/instrument_editor/visualized_instrument_macro_editor.hpp" +#include +#include namespace inst_edit_utils { -SequenceType convertSequenceTypeForData(VisualizedInstrumentMacroEditor::SequenceType type); -VisualizedInstrumentMacroEditor::SequenceType convertSequenceTypeForUI(SequenceType type); -ReleaseType convertReleaseTypeForData(VisualizedInstrumentMacroEditor::ReleaseType type); -VisualizedInstrumentMacroEditor::ReleaseType convertReleaseTypeForUI(ReleaseType type); +QString generateUsersString(const std::multiset& users); } #endif // INSTRUMENT_EDITOR_UTILS_HPP diff -Nru bambootracker-0.4.5+git20121209/BambooTracker/gui/instrument_editor/instrument_form_manager.cpp bambootracker-0.4.6/BambooTracker/gui/instrument_editor/instrument_form_manager.cpp --- bambootracker-0.4.5+git20121209/BambooTracker/gui/instrument_editor/instrument_form_manager.cpp 2020-12-08 14:05:28.000000000 +0000 +++ bambootracker-0.4.6/BambooTracker/gui/instrument_editor/instrument_form_manager.cpp 2021-02-11 14:37:17.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (C) 2018-2020 Rerrah + * Copyright (C) 2018-2021 Rerrah * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation @@ -31,9 +31,7 @@ #include "gui/instrument_editor/instrument_editor_ssg_form.hpp" #include "gui/instrument_editor/instrument_editor_adpcm_form.hpp" #include "gui/instrument_editor/instrument_editor_drumkit_form.hpp" -#include "misc.hpp" - -InstrumentFormManager::InstrumentFormManager() {} +#include "utils.hpp" void InstrumentFormManager::updateByConfiguration() { @@ -186,8 +184,7 @@ int InstrumentFormManager::checkActivatedFormNumber() const { const QWidget* win = QApplication::activeWindow(); - auto it = std::find_if(map_.begin(), map_.end(), - [win](const std::pair> p) { + auto it = utils::findIf(map_, [win](const std::pair> p) { return p.second.get() == win; }); diff -Nru bambootracker-0.4.5+git20121209/BambooTracker/gui/instrument_editor/instrument_form_manager.hpp bambootracker-0.4.6/BambooTracker/gui/instrument_editor/instrument_form_manager.hpp --- bambootracker-0.4.5+git20121209/BambooTracker/gui/instrument_editor/instrument_form_manager.hpp 2020-12-08 14:05:28.000000000 +0000 +++ bambootracker-0.4.6/BambooTracker/gui/instrument_editor/instrument_form_manager.hpp 2021-02-11 14:37:17.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (C) 2018-2020 Rerrah + * Copyright (C) 2018-2021 Rerrah * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation @@ -31,16 +31,15 @@ #include #include #include "instrument/envelope_fm.hpp" +#include "bamboo_tracker_defs.hpp" enum class InstrumentType; -enum class SoundSource; class InstrumentFormManager : public QObject { Q_OBJECT public: - InstrumentFormManager(); void updateByConfiguration(); const std::shared_ptr getForm(int n) const; diff -Nru bambootracker-0.4.5+git20121209/BambooTracker/gui/instrument_editor/visualized_instrument_macro_editor.cpp bambootracker-0.4.6/BambooTracker/gui/instrument_editor/visualized_instrument_macro_editor.cpp --- bambootracker-0.4.5+git20121209/BambooTracker/gui/instrument_editor/visualized_instrument_macro_editor.cpp 2020-12-08 14:05:28.000000000 +0000 +++ bambootracker-0.4.6/BambooTracker/gui/instrument_editor/visualized_instrument_macro_editor.cpp 2021-02-11 14:37:17.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (C) 2018-2020 Rerrah + * Copyright (C) 2018-2021 Rerrah * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation @@ -28,6 +28,8 @@ #include #include #include +#include +#include #include #include #include @@ -35,6 +37,7 @@ #include #include #include "gui/event_guard.hpp" +#include "enum_hash.hpp" VisualizedInstrumentMacroEditor::VisualizedInstrumentMacroEditor(QWidget *parent) : QWidget(parent), @@ -45,10 +48,10 @@ defaultRow_(0), hovRow_(-1), hovCol_(-1), - type_(NoType), - permittedReleaseType_(ReleaseType::FIXED_RELEASE), + type_(SequenceType::PlainSequence), + permittedReleaseType_(PermittedReleaseFlag::FIXED_RELEASE), isLabelOmitted_(false), - release_{ ReleaseType::NO_RELEASE, -1 }, + release_(InstrumentSequenceRelease::NoRelease), ui(new Ui::VisualizedInstrumentMacroEditor), pressRow_(-1), pressCol_(-1), @@ -136,18 +139,18 @@ return static_cast(cols_.size()); } -void VisualizedInstrumentMacroEditor::setSequenceCommand(int row, int col, QString str, int data) +void VisualizedInstrumentMacroEditor::setSequenceData(int row, int col, QString str, int subdata) { size_t idx = static_cast(col); cols_.at(idx).row = row; cols_.at(idx).text = str; - cols_.at(idx).data = data; + cols_.at(idx).data = subdata; ui->panel->update(); printMML(); - emit sequenceCommandChanged(row, col); + emit sequenceDataChanged(row, col); } void VisualizedInstrumentMacroEditor::setText(int col, QString text) @@ -155,9 +158,9 @@ cols_.at(static_cast(col)).text = text; } -void VisualizedInstrumentMacroEditor::setData(int col, int data) +void VisualizedInstrumentMacroEditor::setSubdata(int col, int subdata) { - cols_.at(static_cast(col)).data = data; + cols_.at(static_cast(col)).data = subdata; printMML(); } @@ -176,9 +179,9 @@ isMultiReleaseState_ = enabled; } -void VisualizedInstrumentMacroEditor::addSequenceCommand(int row, QString str, int data) +void VisualizedInstrumentMacroEditor::addSequenceData(int row, QString str, int subdata) { - cols_.push_back({ row, data, str }); + cols_.push_back({ row, subdata, str }); updateColumnWidth(); ui->panel->update(); @@ -187,10 +190,10 @@ printMML(); - emit sequenceCommandAdded(row, static_cast(cols_.size()) - 1); + emit sequenceDataAdded(row, static_cast(cols_.size()) - 1); } -void VisualizedInstrumentMacroEditor::removeSequenceCommand() +void VisualizedInstrumentMacroEditor::removeSequenceData() { if (cols_.size() == 1) return; @@ -209,8 +212,8 @@ } // Modify release - if (release_.point >= static_cast(cols_.size())) - release_.point = -1; + if (release_.getBeginPos() >= static_cast(cols_.size())) + release_.disable(); updateColumnWidth(); ui->panel->update(); @@ -219,7 +222,7 @@ printMML(); - emit sequenceCommandRemoved(); + emit sequenceDataRemoved(); } void VisualizedInstrumentMacroEditor::addLoop(int begin, int end, int times) @@ -237,7 +240,7 @@ printMML(); - onLoopChanged(); + emit loopAdded(InstrumentSequenceLoop(begin, end, times)); } void VisualizedInstrumentMacroEditor::setSequenceType(SequenceType type) @@ -253,10 +256,9 @@ permittedReleaseType_ = types; } -void VisualizedInstrumentMacroEditor::setRelease(ReleaseType type, int point) +void VisualizedInstrumentMacroEditor::setRelease(const InstrumentSequenceRelease& release) { - release_ = { type, point }; - + release_ = release; printMML(); } @@ -264,7 +266,7 @@ { cols_.clear(); loops_.clear(); - release_ = { ReleaseType::NO_RELEASE, -1 }; + release_.disable(); updateColumnWidth(); printMML(); @@ -398,15 +400,16 @@ for (int i = 0; i < seqLen; ++i) { for (size_t j = 0; j < loops_.size(); ++j) { if (loops_[j].begin <= i && i <= loops_[j].end) { - painter.fillRect(w, loopY_, colWidths_[static_cast(i)], - fontHeight_, palette_->instSeqLoopColor); + int colWidth = colWidths_[static_cast(i)]; + painter.fillRect(w, loopY_, colWidth, fontHeight_, palette_->instSeqLoopColor); if (loops_[j].begin == i) { painter.fillRect(w, loopY_, 2, fontHeight_, palette_->instSeqLoopEdgeColor); QString times = (loops_[j].times == 1) ? "" : QString::number(loops_[j].times); - painter.drawText(w + 2, loopBaseY_, tr("Loop %1").arg(times)); + painter.drawText(QRectF(w + 2, loopY_, colWidth - 2, fontHeight_), + Qt::AlignLeft + Qt::AlignVCenter, tr("Loop %1").arg(times)); } if (loops_[j].end == i) { - painter.fillRect(w + colWidths_[static_cast(i)] - 2, loopY_, 2, + painter.fillRect(w + colWidth - 2, loopY_, 2, fontHeight_, palette_->instSeqLoopEdgeColor); } } @@ -429,29 +432,21 @@ painter.setPen(palette_->instSeqReleaseTextColor); painter.drawText(1, releaseBaseY_, tr("Release")); + static const std::unordered_map DISP_TEXT_MAP = { + { InstrumentSequenceRelease::NoRelease, "" }, + { InstrumentSequenceRelease::FixedRelease, tr("Fixed") }, + { InstrumentSequenceRelease::AbsoluteRelease, tr("Absolute") }, + { InstrumentSequenceRelease::RelativeRelease, tr("Relative") } + }; + int w = labWidth_; int seqLen = static_cast(cols_.size()); for (int i = 0; i < seqLen; ++i) { - if (release_.point == i) { + if (release_.getBeginPos() == i) { painter.fillRect(w, releaseY_, panelWidth() - w, fontHeight_, palette_->instSeqReleaseColor); painter.fillRect(w, releaseY_, 2, fontHeight_, palette_->instSeqReleaseEdgeColor); - QString type; - switch (release_.type) { - case ReleaseType::NO_RELEASE: - type = ""; - break; - case ReleaseType::FIXED_RELEASE: - type = tr("Fixed"); - break; - case ReleaseType::ABSOLUTE_RELEASE: - type = tr("Absolute"); - break; - case ReleaseType::RELATIVE_RELEASE: - type = tr("Relative"); - break; - } painter.setPen(palette_->instSeqReleaseTextColor); - painter.drawText(w + 2, releaseBaseY_, type); + painter.drawText(w + 2, releaseBaseY_, DISP_TEXT_MAP.at(release_.getType())); } if (hovRow_ == -3 && hovCol_ == i) @@ -490,15 +485,17 @@ QString text = ""; std::vector lstack; + static const std::unordered_map DISP_REL_MAP = { + { InstrumentSequenceRelease::NoRelease, "" }, + { InstrumentSequenceRelease::FixedRelease, "| " }, + { InstrumentSequenceRelease::AbsoluteRelease, "/ " }, + { InstrumentSequenceRelease::RelativeRelease, ": " } + }; + int seqLen = static_cast(cols_.size()); for (int cnt = 0; cnt < seqLen; ++cnt) { - if (release_.point == cnt) { - switch (release_.type) { - case ReleaseType::FIXED_RELEASE: text += "| "; break; - case ReleaseType::ABSOLUTE_RELEASE: text += "/ "; break; - case ReleaseType::RELATIVE_RELEASE: text += ": "; break; - default: break; - } + if (release_.getBeginPos() == cnt) { + text += DISP_REL_MAP.at(release_.getType()); } for (size_t i = 0; i < loops_.size(); ++i) { @@ -544,8 +541,12 @@ std::vector column; std::vector loop; + InstrumentSequenceLoopRoot loopRoot(1); + + std::deque lbstack; + std::deque loopStack; + InstrumentSequenceRelease release(InstrumentSequenceRelease::NoRelease); std::vector lstack; - Release release = { ReleaseType::NO_RELEASE, -1 }; int cnt = 0; while (!text.isEmpty()) { @@ -554,6 +555,7 @@ if (m.hasMatch()) { loop.push_back({ cnt, cnt, 1 }); lstack.push_back(loop.size() - 1); + lbstack.push_back(cnt); text.remove(QRegularExpression("^\\[")); continue; } @@ -568,43 +570,62 @@ else return; } lstack.pop_back(); + if (lbstack.empty() || lbstack.back() == cnt) return; + if (!m.captured(1).isEmpty()) { + int t = m.captured(1).toInt(); + if (t > 1) loopStack.push_back(InstrumentSequenceLoop(lbstack.back(), cnt - 1, t)); + else return; + } + else { + loopStack.push_back(InstrumentSequenceLoop(lbstack.back(), cnt - 1)); + } + lbstack.pop_back(); text.remove(QRegularExpression("^\\]\\d*")); continue; } - if (permittedReleaseType_ & ReleaseType::FIXED_RELEASE) { + if (permittedReleaseType_ & PermittedReleaseFlag::FIXED_RELEASE) { m = QRegularExpression("^\\|").match(text); if (m.hasMatch()) { - if (release.point > -1) return; - release = { ReleaseType::FIXED_RELEASE, cnt }; + if (release.isEnabled()) return; + release.setType(InstrumentSequenceRelease::FixedRelease); + release.setBeginPos(cnt); text.remove(QRegularExpression("^\\|")); continue; } } - if (permittedReleaseType_ & ReleaseType::ABSOLUTE_RELEASE) { + if (permittedReleaseType_ & PermittedReleaseFlag::ABSOLUTE_RELEASE) { m = QRegularExpression("^/").match(text); if (m.hasMatch()) { - if (release.point > -1) return; - release = { ReleaseType::ABSOLUTE_RELEASE, cnt }; + if (release.isEnabled()) return; + release.setType(InstrumentSequenceRelease::AbsoluteRelease); + release.setBeginPos(cnt); text.remove(QRegularExpression("^/")); continue; } } - if (permittedReleaseType_ & ReleaseType::RELATIVE_RELEASE) { + if (permittedReleaseType_ & PermittedReleaseFlag::RELATIVE_RELEASE) { m = QRegularExpression("^:").match(text); if (m.hasMatch()) { - if (release.point > -1) return; - release = { ReleaseType::RELATIVE_RELEASE, cnt }; + if (release.isEnabled()) return; + release.setType(InstrumentSequenceRelease::RelativeRelease); + release.setBeginPos(cnt); text.remove(QRegularExpression("^:")); continue; } } - if (interpretSlopeInMML(text, cnt, column)) continue; // Slope + if (interpretSlopeInMML(text, cnt, column)) { // Slope + loopRoot.resize(cnt); + continue; + } - if (interpretDataInMML(text, cnt, column)) continue; // Data + if (interpretDataInMML(text, cnt, column)) { // Data + loopRoot.resize(cnt); + continue; + } m = QRegularExpression("^ +").match(text); if (m.hasMatch()) { @@ -617,19 +638,24 @@ if (column.empty()) return; if (!lstack.empty()) return; - if (release.point > -1 && release.point >= static_cast(column.size())) return; + if (!lbstack.empty()) return; + if (release.isEnabled() && release.getBeginPos() >= static_cast(column.size())) return; - while (cols_.size() > 1) removeSequenceCommand(); - setSequenceCommand(column.front().row, 0); + while (cols_.size() > 1) removeSequenceData(); + setSequenceData(column.front().row, 0); for (size_t i = 1; i < column.size(); ++i) { - addSequenceCommand(column[i].row); + addSequenceData(column[i].row); } loops_ = loop; - onLoopChanged(); + emit loopCleared(); + // Reverse order because deepest is first in + for (auto itr = loopStack.crbegin(); itr != loopStack.crend(); ++itr) { + emit loopAdded(*itr); + } release_ = release; - emit releaseChanged(release.type, release.point); + emit releaseChanged(release); ui->panel->update(); } @@ -723,44 +749,58 @@ { if (hovCol_ < 0) return; - size_t idx = static_cast(grabLoop_); + size_t glabIdxU = static_cast(grabLoop_); + auto& tgtLoopRef = loops_[glabIdxU]; + int prevBegin = tgtLoopRef.begin; + int prevEnd = tgtLoopRef.end; + if (isGrabLoopHead_) { - if (hovCol_ < loops_[idx].begin) { - if (grabLoop_ > 0 && loops_[idx - 1].end >= hovCol_) { - loops_[idx].begin = loops_[idx - 1].end + 1; + if (hovCol_ < prevBegin) { + if (glabIdxU && hovCol_ <= loops_[glabIdxU - 1].end) { + tgtLoopRef.begin = loops_[glabIdxU - 1].end + 1; } else { - loops_[idx].begin = hovCol_; + tgtLoopRef.begin = hovCol_; } } - else if (hovCol_ > loops_[idx].begin) { - if (hovCol_ > loops_[idx].end) { + else if (prevBegin < hovCol_) { + if (prevEnd < hovCol_) { loops_.erase(loops_.begin() + grabLoop_); + emit loopRemoved(prevBegin, prevEnd); + printMML(); + return; } else { - loops_[idx].begin = hovCol_; + tgtLoopRef.begin = hovCol_; } } + else return; } else { - if (hovCol_ < loops_[idx].end) { - if (hovCol_ < loops_[idx].begin) { + if (hovCol_ < prevEnd) { + if (hovCol_ < prevBegin) { loops_.erase(loops_.begin() + grabLoop_); + emit loopRemoved(prevBegin, prevEnd); + printMML(); + return; } else { - loops_[idx].end = hovCol_; + tgtLoopRef.end = hovCol_; } } - else if (hovCol_ > loops_[idx].end) { - if (grabLoop_ < static_cast(loops_.size()) - 1 && loops_[idx + 1].begin <= hovCol_) { - loops_[idx].end = loops_[idx + 1].begin - 1; + else if (prevEnd < hovCol_) { + if (glabIdxU < loops_.size() - 1 && loops_[glabIdxU + 1].begin <= hovCol_) { + tgtLoopRef.end = loops_[glabIdxU + 1].begin - 1; } else { - loops_[idx].end = hovCol_; + tgtLoopRef.end = hovCol_; } } + else return; } + emit loopChanged(prevBegin, prevEnd, + InstrumentSequenceLoop(tgtLoopRef.begin, tgtLoopRef.end, tgtLoopRef.times)); printMML(); } @@ -832,7 +872,7 @@ if (!isEnabled()) drawShadow(); QPainter painter(ui->panel); - painter.drawPixmap(event->rect(), pixmap_); + painter.drawPixmap(event->rect(), pixmap_, event->rect()); } void VisualizedInstrumentMacroEditor::resizeEventInView(QResizeEvent* event) @@ -898,9 +938,9 @@ } } } - else if (hovRow_ == -3 && release_.point != -1) { + else if (hovRow_ == -3 && release_.isEnabled()) { if (event->button() == Qt::LeftButton) { - int w = labWidth_ + std::accumulate(colWidths_.begin(), colWidths_.begin() + release_.point, 0); + int w = labWidth_ + std::accumulate(colWidths_.begin(), colWidths_.begin() + release_.getBeginPos(), 0); if (w - 4 < x && x < w + 4) { isGrabRelease_ = true; } @@ -921,7 +961,8 @@ else { // Loop count up ++loops_[static_cast(i)].times; printMML(); - onLoopChanged(); + auto& l = loops_[static_cast(i)]; + emit loopChanged(l.begin, l.end, InstrumentSequenceLoop(l.begin, l.end, l.times)); } break; } @@ -930,12 +971,15 @@ if (i > -1) { // Loop count down if (loops_[static_cast(i)].times > 1) { --loops_[static_cast(i)].times; + auto& l = loops_[static_cast(i)]; + emit loopChanged(l.begin, l.end, InstrumentSequenceLoop(l.begin, l.end, l.times)); } else { // Erase loop + auto& l = loops_[static_cast(i)]; + emit loopRemoved(l.begin, l.end); loops_.erase(loops_.begin() + i); } printMML(); - onLoopChanged(); } break; } @@ -949,38 +993,36 @@ switch (event->button()) { case Qt::LeftButton: { - if (release_.point == -1 || pressCol_ < release_.point) { // New release - release_.type = (release_.type == ReleaseType::NO_RELEASE) - ? ReleaseType::FIXED_RELEASE - : release_.type; - release_.point = pressCol_; + if (!release_.isEnabled() || pressCol_ < release_.getBeginPos()) { // New release + if (!release_.isEnabled()) release_.setType(InstrumentSequenceRelease::FixedRelease); + release_.setBeginPos(pressCol_); printMML(); - emit releaseChanged(release_.type, release_.point); + emit releaseChanged(release_); } else if (isMultiReleaseState_) { // Change release type - switch (release_.type) { - case ReleaseType::FIXED_RELEASE: - release_.type = ReleaseType::ABSOLUTE_RELEASE; + switch (release_.getType()) { + case InstrumentSequenceRelease::FixedRelease: + release_.setType(InstrumentSequenceRelease::AbsoluteRelease); break; - case ReleaseType::ABSOLUTE_RELEASE: - release_.type = ReleaseType::RELATIVE_RELEASE; + case InstrumentSequenceRelease::AbsoluteRelease: + release_.setType(InstrumentSequenceRelease::RelativeRelease); break; - case ReleaseType::NO_RELEASE: - case ReleaseType::RELATIVE_RELEASE: - release_.type = ReleaseType::FIXED_RELEASE; + case InstrumentSequenceRelease::NoRelease: + case InstrumentSequenceRelease::RelativeRelease: + release_.setType(InstrumentSequenceRelease::FixedRelease); break; } printMML(); - emit releaseChanged(release_.type, release_.point); + emit releaseChanged(release_); } break; } case Qt::RightButton: { - if (pressCol_ >= release_.point) { // Erase release - release_ = { ReleaseType::NO_RELEASE, -1 }; + if (pressCol_ >= release_.getBeginPos()) { // Erase release + release_.disable(); printMML(); - emit releaseChanged(release_.type, release_.point); + emit releaseChanged(release_); } break; } @@ -990,7 +1032,7 @@ } } else { - setSequenceCommand(detectRowNumberForMouseEvent(hovCol_, hovRow_), hovCol_); + setSequenceData(detectRowNumberForMouseEvent(hovCol_, hovRow_), hovCol_); prevPressRow_ = hovRow_; prevPressCol_ = hovCol_; } @@ -1006,15 +1048,14 @@ if (grabLoop_ != -1) { // Move loop if (event->button() == Qt::LeftButton) { moveLoop(); - onLoopChanged(); } } else if (isGrabRelease_) { // Move release if (event->button() == Qt::LeftButton) { if (hovCol_ > -1) { - release_.point = hovCol_; + release_.setBeginPos(hovCol_); printMML(); - emit releaseChanged(release_.type, release_.point); + emit releaseChanged(release_); } } } @@ -1038,7 +1079,7 @@ if (prevPressRow_ != hovRow_ || prevPressCol_ != hovCol_) { prevPressRow_ = hovRow_; prevPressCol_ = hovCol_; - setSequenceCommand(detectRowNumberForMouseEvent(hovCol_, hovRow_), hovCol_); + setSequenceData(detectRowNumberForMouseEvent(hovCol_, hovRow_), hovCol_); } } } @@ -1114,12 +1155,12 @@ void VisualizedInstrumentMacroEditor::on_colIncrToolButton_clicked() { - addSequenceCommand(defaultRow_); + addSequenceData(defaultRow_); } void VisualizedInstrumentMacroEditor::on_colDecrToolButton_clicked() { - removeSequenceCommand(); + removeSequenceData(); } void VisualizedInstrumentMacroEditor::on_verticalScrollBar_valueChanged(int value) @@ -1141,18 +1182,6 @@ printMML(); } -void VisualizedInstrumentMacroEditor::onLoopChanged() -{ - std::vector begins, ends, times; - for (auto& l : loops_) { - begins.push_back(l.begin); - ends.push_back(l.end); - times.push_back(l.times); - } - - emit loopChanged(std::move(begins), std::move(ends), std::move(times)); -} - void VisualizedInstrumentMacroEditor::updateColumnWidth() { colWidths_.clear(); diff -Nru bambootracker-0.4.5+git20121209/BambooTracker/gui/instrument_editor/visualized_instrument_macro_editor.hpp bambootracker-0.4.6/BambooTracker/gui/instrument_editor/visualized_instrument_macro_editor.hpp --- bambootracker-0.4.5+git20121209/BambooTracker/gui/instrument_editor/visualized_instrument_macro_editor.hpp 2020-12-08 14:05:28.000000000 +0000 +++ bambootracker-0.4.6/BambooTracker/gui/instrument_editor/visualized_instrument_macro_editor.hpp 2021-02-11 14:37:17.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (C) 2018-2020 Rerrah + * Copyright (C) 2018-2021 Rerrah * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation @@ -40,10 +40,11 @@ #include #include #include +#include "instrument/sequence_property.hpp" #include "gui/color_palette.hpp" namespace Ui { - class VisualizedInstrumentMacroEditor; +class VisualizedInstrumentMacroEditor; } class VisualizedInstrumentMacroEditor : public QWidget @@ -61,31 +62,25 @@ void setDefaultRow(int row); int getSequenceLength() const; - void setSequenceCommand(int row, int col, QString str = "", int data = -1); + void setSequenceData(int row, int col, QString str = "", int subdata = -1); void setText(int col, QString text); - void setData(int col, int data); + void setSubdata(int col, int subdata); int getSequenceAt(int col) const; int getSequenceDataAt(int col) const; void setMultipleReleaseState(bool enabled); - void addSequenceCommand(int row, QString str = "", int data = -1); - void removeSequenceCommand(); + void addSequenceData(int row, QString str = "", int subdata = -1); + void removeSequenceData(); + void clearSequenceData(); + // NOTE: It is desirable to use loop class void addLoop(int begin, int end, int times); - enum SequenceType - { - NoType, - FixedSequence, - AbsoluteSequence, - RelativeSequence - }; - void setSequenceType(SequenceType type); - enum ReleaseType : int + enum PermittedReleaseFlag : int { NO_RELEASE = 0, FIXED_RELEASE = 1, @@ -94,7 +89,7 @@ }; void setPermittedReleaseTypes(int types); - void setRelease(ReleaseType type, int point); + void setRelease(const InstrumentSequenceRelease& release); void clearData(); void clearRow(); @@ -109,11 +104,14 @@ void setMMLDisplay0As(int n); signals: - void sequenceCommandChanged(int row, int col); - void sequenceCommandAdded(int row, int col); - void sequenceCommandRemoved(); - void loopChanged(std::vector begins, std::vector ends, std::vector times); - void releaseChanged(ReleaseType type, int point); + void sequenceDataChanged(int row, int col); + void sequenceDataAdded(int row, int col); + void sequenceDataRemoved(); + void loopCleared(); + void loopAdded(InstrumentSequenceLoop loop); + void loopRemoved(int begin, int end); + void loopChanged(int prevBegin, int prevEnd, InstrumentSequenceLoop loop); + void releaseChanged(InstrumentSequenceRelease release); protected: bool eventFilter(QObject*object, QEvent* event) override; @@ -162,19 +160,14 @@ virtual int detectRowNumberForMouseEvent(int col, int internalRow) const; + // NOTE: It is desirable to use loop class struct Loop { int begin, end, times; }; - struct Release - { - ReleaseType type; - int point; - }; - std::vector loops_; - Release release_; + InstrumentSequenceRelease release_; void printMML(); @@ -197,7 +190,6 @@ void on_colDecrToolButton_clicked(); void on_verticalScrollBar_valueChanged(int value); void on_lineEdit_editingFinished(); - void onLoopChanged(); private: Ui::VisualizedInstrumentMacroEditor *ui; diff -Nru bambootracker-0.4.5+git20121209/BambooTracker/gui/instrument_selection_dialog.cpp bambootracker-0.4.6/BambooTracker/gui/instrument_selection_dialog.cpp --- bambootracker-0.4.5+git20121209/BambooTracker/gui/instrument_selection_dialog.cpp 2020-12-08 14:05:28.000000000 +0000 +++ bambootracker-0.4.6/BambooTracker/gui/instrument_selection_dialog.cpp 2021-02-11 14:37:17.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (C) 2018-2020 Rerrah + * Copyright (C) 2018-2021 Rerrah * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation @@ -65,7 +65,7 @@ QVector InstrumentSelectionDialog::currentInstrumentSelection() const { QListWidget *lw = ui_->listWidget; - QList items = lw->selectedItems(); + const QList items = lw->selectedItems(); QVector selection; selection.reserve(items.size()); diff -Nru bambootracker-0.4.5+git20121209/BambooTracker/gui/jam_layout.hpp bambootracker-0.4.6/BambooTracker/gui/jam_layout.hpp --- bambootracker-0.4.5+git20121209/BambooTracker/gui/jam_layout.hpp 2020-12-08 14:05:28.000000000 +0000 +++ bambootracker-0.4.6/BambooTracker/gui/jam_layout.hpp 2021-02-11 14:37:17.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (C) 2020 Rerrah + * Copyright (C) 2020-2021 Rerrah * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation @@ -31,6 +31,7 @@ #include #include "configuration.hpp" #include "jamming.hpp" +#include "utils.hpp" // Layout decipherer inline JamKey getJamKeyFromLayoutMapping(Qt::Key key, std::weak_ptr config) @@ -39,7 +40,7 @@ Configuration::KeyboardLayout selectedLayout = configLocked->getNoteEntryLayout(); if (configLocked->mappingLayouts.find(selectedLayout) != configLocked->mappingLayouts.end()) { std::unordered_map selectedLayoutMapping = configLocked->mappingLayouts.at(selectedLayout); - auto it = std::find_if(selectedLayoutMapping.begin(), selectedLayoutMapping.end(), + auto it = utils::findIf(selectedLayoutMapping, [key](const std::pair& t) -> bool { return (QKeySequence(key).matches(QKeySequence(QString::fromStdString(t.first))) == QKeySequence::ExactMatch); }); diff -Nru bambootracker-0.4.5+git20121209/BambooTracker/gui/mainwindow.cpp bambootracker-0.4.6/BambooTracker/gui/mainwindow.cpp --- bambootracker-0.4.5+git20121209/BambooTracker/gui/mainwindow.cpp 2020-12-08 14:05:28.000000000 +0000 +++ bambootracker-0.4.6/BambooTracker/gui/mainwindow.cpp 2021-02-11 14:37:17.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (C) 2018-2020 Rerrah + * Copyright (C) 2018-2021 Rerrah * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation @@ -27,6 +27,7 @@ #include "ui_mainwindow.h" #include #include +#include #include #include #include @@ -59,7 +60,7 @@ #include "io/binary_container.hpp" #include "io/wav_container.hpp" #include "version.hpp" -#include "gui/command/commands_qt.hpp" +#include "gui/command/instrument/instrument_commands_qt.hpp" #include "gui/instrument_editor/instrument_editor_fm_form.hpp" #include "gui/instrument_editor/instrument_editor_ssg_form.hpp" #include "gui/instrument_editor/instrument_editor_adpcm_form.hpp" @@ -87,6 +88,17 @@ #include "gui/track_visibility_memory_handler.hpp" #include "gui/file_io_error_message_box.hpp" #include "gui/gui_utils.hpp" +#include "utils.hpp" + +namespace +{ +const std::unordered_map TB_POS_ = { + { Configuration::ToolbarPosition::TopPosition, Qt::TopToolBarArea }, + { Configuration::ToolbarPosition::BottomPosition, Qt::BottomToolBarArea }, + { Configuration::ToolbarPosition::LeftPosition, Qt::LeftToolBarArea }, + { Configuration::ToolbarPosition::RightPosition, Qt::RightToolBarArea } +}; +} MainWindow::MainWindow(std::weak_ptr config, QString filePath, QWidget *parent) : QMainWindow(parent), @@ -135,7 +147,7 @@ if (config.lock()->getMainWindowX() == -1) { // When unset QRect rec = geometry(); - rec.moveCenter(QGuiApplication::screens().front()->geometry().center()); + rec.moveCenter(QGuiApplication::screens().at(0)->geometry().center()); setGeometry(rec); config.lock()->setMainWindowX(x()); config.lock()->setMainWindowY(y()); @@ -222,20 +234,20 @@ pasteModeGroup_ = std::make_unique(this); pasteModeGroup_->addAction(ui->action_Cursor); QObject::connect(ui->action_Cursor, &QAction::triggered, this, [&](bool checked) { - if (checked) config_.lock()->setPasteMode(Configuration::CURSOR); + if (checked) config_.lock()->setPasteMode(Configuration::PasteMode::Cursor); }); pasteModeGroup_->addAction(ui->action_Selection); QObject::connect(ui->action_Selection, &QAction::triggered, this, [&](bool checked) { - if (checked) config_.lock()->setPasteMode(Configuration::SELECTION); + if (checked) config_.lock()->setPasteMode(Configuration::PasteMode::Selection); }); pasteModeGroup_->addAction(ui->action_Fill); QObject::connect(ui->action_Fill, &QAction::triggered, this, [&](bool checked) { - if (checked) config_.lock()->setPasteMode(Configuration::FILL); + if (checked) config_.lock()->setPasteMode(Configuration::PasteMode::Fill); }); switch (config.lock()->getPasteMode()) { - case Configuration::CURSOR: ui->action_Cursor->setChecked(true); break; - case Configuration::SELECTION: ui->action_Selection->setChecked(true); break; - case Configuration::FILL: ui->action_Fill->setChecked(true); break; + case Configuration::PasteMode::Cursor: ui->action_Cursor->setChecked(true); break; + case Configuration::PasteMode::Selection: ui->action_Selection->setChecked(true); break; + case Configuration::PasteMode::Fill: ui->action_Fill->setChecked(true); break; } /* Tool bars */ @@ -303,7 +315,7 @@ ui->subToolBar->addWidget(highlight2_); ui->subToolBar->addSeparator(); auto& mainTbConfig = config.lock()->getMainToolbarConfiguration(); - if (mainTbConfig.getPosition() == Configuration::ToolbarConfiguration::FLOAT_POS) { + if (mainTbConfig.getPosition() == Configuration::ToolbarPosition::FloatPorition) { ui->mainToolBar->setWindowFlags(Qt::Tool | Qt::FramelessWindowHint | Qt::X11BypassWindowManagerHint); ui->mainToolBar->move(mainTbConfig.getX(), mainTbConfig.getY()); } @@ -311,7 +323,7 @@ addToolBar(TB_POS_.at(mainTbConfig.getPosition()), ui->mainToolBar); } auto& subTbConfig = config.lock()->getSubToolbarConfiguration(); - if (subTbConfig.getPosition() == Configuration::ToolbarConfiguration::FLOAT_POS) { + if (subTbConfig.getPosition() == Configuration::ToolbarPosition::FloatPorition) { ui->subToolBar->setWindowFlags(Qt::Tool | Qt::FramelessWindowHint | Qt::X11BypassWindowManagerHint); ui->subToolBar->move(subTbConfig.getX(), subTbConfig.getY()); } @@ -762,7 +774,7 @@ SongType memSongType; std::vector visTracks; if (config.lock()->getRestoreTrackVisibility() - && TrackVisibilityMemoryHandler::loadTrackVisibilityMemory(memSongType, visTracks)) { + && io::loadTrackVisibilityMemory(memSongType, visTracks)) { SongType songType = bt_->getSongStyle(bt_->getCurrentSongNumber()).type; visTracks = gui_utils::adaptVisibleTrackList(visTracks, songType, songType); } @@ -853,7 +865,7 @@ if (!hasShownOnce_) { int y = config_.lock()->getMainWindowVerticalSplit(); if (y == -1) { - config_.lock()->setMainWindowVerticalSplit(ui->splitter->sizes().front()); + config_.lock()->setMainWindowVerticalSplit(ui->splitter->sizes().at(0)); } else { ui->splitter->setSizes({ y, ui->splitter->height() - ui->splitter->handleWidth() - y }); @@ -891,7 +903,7 @@ { auto mime = event->mimeData(); if (mime->hasUrls() && mime->urls().length() == 1) { - const std::string ext = QFileInfo(mime->urls().first().toLocalFile()).suffix().toLower().toStdString(); + const std::string ext = QFileInfo(mime->urls().at(0).toLocalFile()).suffix().toLower().toStdString(); if (io::ModuleIO::getInstance().testLoadableFormat(ext) | io::InstrumentIO::getInstance().testLoadableFormat(ext) | io::BankIO::getInstance().testLoadableFormat(ext)) @@ -994,33 +1006,29 @@ config_.lock()->setMainWindowX(x()); config_.lock()->setMainWindowY(y()); } - config_.lock()->setMainWindowVerticalSplit(ui->splitter->sizes().front()); + config_.lock()->setMainWindowVerticalSplit(ui->splitter->sizes().at(0)); auto& mainTbConfig = config_.lock()->getMainToolbarConfiguration(); auto& subTbConfig = config_.lock()->getSubToolbarConfiguration(); auto mainTbArea = toolBarArea(ui->mainToolBar); auto subTbArea = toolBarArea(ui->subToolBar); if (ui->mainToolBar->isFloating()) { - mainTbConfig.setPosition(ToolbarPos::FLOAT_POS); + mainTbConfig.setPosition(Configuration::ToolbarPosition::FloatPorition); mainTbConfig.setX(ui->mainToolBar->x()); mainTbConfig.setY(ui->mainToolBar->y()); } else { - mainTbConfig.setPosition(std::find_if(TB_POS_.begin(), TB_POS_.end(), [mainTbArea](auto pair) { - return (pair.second == mainTbArea); - })->first); + mainTbConfig.setPosition(utils::findMapValue(TB_POS_, mainTbArea)->first); mainTbConfig.setNumber(0); mainTbConfig.setBreakBefore(false); } if (ui->subToolBar->isFloating()) { - subTbConfig.setPosition(ToolbarPos::FLOAT_POS); + subTbConfig.setPosition(Configuration::ToolbarPosition::FloatPorition); subTbConfig.setX(ui->subToolBar->x()); subTbConfig.setY(ui->subToolBar->y()); } else { - subTbConfig.setPosition(std::find_if(TB_POS_.begin(), TB_POS_.end(), [subTbArea](auto pair) { - return (pair.second == subTbArea); - })->first); + subTbConfig.setPosition(utils::findMapValue(TB_POS_, mainTbArea)->first); subTbConfig.setNumber(0); subTbConfig.setBreakBefore(false); } @@ -1111,7 +1119,7 @@ instForms_->closeAll(); FileHistoryHandler::saveFileHistory(fileHistory_); - TrackVisibilityMemoryHandler::saveTrackVisibilityMemory( + io::saveTrackVisibilityMemory( bt_->getSongStyle(bt_->getCurrentSongNumber()).type, ui->patternEditor->getVisibleTracks()); @@ -1138,68 +1146,68 @@ void MainWindow::setShortcuts() { auto shortcuts = config_.lock()->getShortcuts(); - octUpSc_.setShortcut(gui_utils::strToKeySeq(shortcuts.at(Configuration::OctaveUp))); - octDownSc_.setShortcut(gui_utils::strToKeySeq(shortcuts.at(Configuration::OctaveDown))); - focusPtnSc_.setKey(gui_utils::strToKeySeq(shortcuts.at(Configuration::FocusOnPattern))); - focusOdrSc_.setKey(gui_utils::strToKeySeq(shortcuts.at(Configuration::FocusOnOrder))); - focusInstSc_.setKey(gui_utils::strToKeySeq(shortcuts.at(Configuration::FocusOnInstrument))); - playAndStopSc_.setShortcut(gui_utils::strToKeySeq(shortcuts.at(Configuration::PlayAndStop))); - ui->actionPlay->setShortcut(gui_utils::strToKeySeq(shortcuts.at(Configuration::Play))); - ui->actionPlay_From_Start->setShortcut(gui_utils::strToKeySeq(shortcuts.at(Configuration::PlayFromStart))); - ui->actionPlay_Pattern->setShortcut(gui_utils::strToKeySeq(shortcuts.at(Configuration::PlayPattern))); - ui->actionPlay_From_Cursor->setShortcut(gui_utils::strToKeySeq(shortcuts.at(Configuration::PlayFromCursor))); - ui->actionPlay_From_Marker->setShortcut(gui_utils::strToKeySeq(shortcuts.at(Configuration::PlayFromMarker))); - playStepSc_.setShortcut(gui_utils::strToKeySeq(shortcuts.at(Configuration::PlayStep))); - ui->actionStop->setShortcut(gui_utils::strToKeySeq(shortcuts.at(Configuration::Stop))); - ui->actionEdit_Mode->setShortcut(gui_utils::strToKeySeq(shortcuts.at(Configuration::ToggleEditJam))); - ui->actionSet_Ro_w_Marker->setShortcut(gui_utils::strToKeySeq(shortcuts.at(Configuration::SetMarker))); - ui->actionMix->setShortcut(gui_utils::strToKeySeq(shortcuts.at(Configuration::PasteMix))); - ui->actionOverwrite->setShortcut(gui_utils::strToKeySeq(shortcuts.at(Configuration::PasteOverwrite))); - ui->action_Insert->setShortcut(gui_utils::strToKeySeq(shortcuts.at(Configuration::PasteInsert))); - ui->actionAll->setShortcut(gui_utils::strToKeySeq(shortcuts.at(Configuration::SelectAll))); - ui->actionNone->setShortcut(gui_utils::strToKeySeq(shortcuts.at(Configuration::Deselect))); - ui->actionRow->setShortcut(gui_utils::strToKeySeq(shortcuts.at(Configuration::SelectRow))); - ui->actionColumn->setShortcut(gui_utils::strToKeySeq(shortcuts.at(Configuration::SelectColumn))); - ui->actionPattern->setShortcut(gui_utils::strToKeySeq(shortcuts.at(Configuration::SelectColumn))); - ui->actionOrder->setShortcut(gui_utils::strToKeySeq(shortcuts.at(Configuration::SelectOrder))); - ui->action_Go_To->setShortcut(gui_utils::strToKeySeq(shortcuts.at(Configuration::GoToStep))); - ui->actionToggle_Track->setShortcut(gui_utils::strToKeySeq(shortcuts.at(Configuration::ToggleTrack))); - ui->actionSolo_Track->setShortcut(gui_utils::strToKeySeq(shortcuts.at(Configuration::SoloTrack))); - ui->actionInterpolate->setShortcut(gui_utils::strToKeySeq(shortcuts.at(Configuration::Interpolate))); - goPrevOdrSc_.setShortcut(gui_utils::strToKeySeq(shortcuts.at(Configuration::GoToPrevOrder))); - goNextOdrSc_.setShortcut(gui_utils::strToKeySeq(shortcuts.at(Configuration::GoToNextOrder))); - ui->action_Toggle_Bookmark->setShortcut(gui_utils::strToKeySeq(shortcuts.at(Configuration::ToggleBookmark))); - ui->action_Previous_Bookmark->setShortcut(gui_utils::strToKeySeq(shortcuts.at(Configuration::PrevBookmark))); - ui->action_Next_Bookmark->setShortcut(gui_utils::strToKeySeq(shortcuts.at(Configuration::NextBookmark))); - ui->actionDecrease_Note->setShortcut(gui_utils::strToKeySeq(shortcuts.at(Configuration::DecreaseNote))); - ui->actionIncrease_Note->setShortcut(gui_utils::strToKeySeq(shortcuts.at(Configuration::IncreaseNote))); - ui->actionDecrease_Octave->setShortcut(gui_utils::strToKeySeq(shortcuts.at(Configuration::DecreaseOctave))); - ui->actionIncrease_Octave->setShortcut(gui_utils::strToKeySeq(shortcuts.at(Configuration::IncreaseOctave))); - prevInstSc_.setShortcut(gui_utils::strToKeySeq(shortcuts.at(Configuration::PrevInstrument))); - nextInstSc_.setShortcut(gui_utils::strToKeySeq(shortcuts.at(Configuration::NextInstrument))); - ui->action_Instrument_Mask->setShortcut(gui_utils::strToKeySeq(shortcuts.at(Configuration::MaskInstrument))); - ui->action_Volume_Mask->setShortcut(gui_utils::strToKeySeq(shortcuts.at(Configuration::MaskVolume))); - ui->actionEdit->setShortcut(gui_utils::strToKeySeq(shortcuts.at(Configuration::EditInstrument))); - ui->actionFollow_Mode->setShortcut(gui_utils::strToKeySeq(shortcuts.at(Configuration::FollowMode))); - ui->actionDuplicate_Order->setShortcut(gui_utils::strToKeySeq(shortcuts.at(Configuration::DuplicateOrder))); - ui->actionClone_Patterns->setShortcut(gui_utils::strToKeySeq(shortcuts.at(Configuration::ClonePatterns))); - ui->actionClone_Order->setShortcut(gui_utils::strToKeySeq(shortcuts.at(Configuration::CloneOrder))); - ui->actionReplace_Instrument->setShortcut(gui_utils::strToKeySeq(shortcuts.at(Configuration::ReplaceInstrument))); - ui->actionExpand->setShortcut(gui_utils::strToKeySeq(shortcuts.at(Configuration::ExpandPattern))); - ui->actionShrink->setShortcut(gui_utils::strToKeySeq(shortcuts.at(Configuration::ShrinkPattern))); - ui->actionFine_Decrease_Values->setShortcut(gui_utils::strToKeySeq(shortcuts.at(Configuration::FineDecreaseValues))); - ui->actionFine_Increase_Values->setShortcut(gui_utils::strToKeySeq(shortcuts.at(Configuration::FineIncreaseValues))); - ui->actionCoarse_D_ecrease_Values->setShortcut(gui_utils::strToKeySeq(shortcuts.at(Configuration::CoarseDecreaseValues))); - ui->actionCoarse_I_ncrease_Values->setShortcut(gui_utils::strToKeySeq(shortcuts.at(Configuration::CoarseIncreaseValuse))); - incPtnSizeSc_.setShortcut(gui_utils::strToKeySeq(shortcuts.at(Configuration::IncreasePatternSize))); - decPtnSizeSc_.setShortcut(gui_utils::strToKeySeq(shortcuts.at(Configuration::DecreasePatternSize))); - incEditStepSc_.setShortcut(gui_utils::strToKeySeq(shortcuts.at(Configuration::IncreaseEditStep))); - decEditStepSc_.setShortcut(gui_utils::strToKeySeq(shortcuts.at(Configuration::DecreaseEditStep))); - ui->action_Effect_List->setShortcut(gui_utils::strToKeySeq(shortcuts.at(Configuration::DisplayEffectList))); - prevSongSc_.setShortcut(gui_utils::strToKeySeq(shortcuts.at(Configuration::PreviousSong))); - nextSongSc_.setShortcut(gui_utils::strToKeySeq(shortcuts.at(Configuration::NextSong))); - jamVolUpSc_.setShortcut(gui_utils::strToKeySeq(shortcuts.at(Configuration::JamVolumeUp))); - jamVolDownSc_.setShortcut(gui_utils::strToKeySeq(shortcuts.at(Configuration::JamVolumeDown))); + octUpSc_.setShortcut(gui_utils::strToKeySeq(shortcuts.at(Configuration::ShortcutAction::OctaveUp))); + octDownSc_.setShortcut(gui_utils::strToKeySeq(shortcuts.at(Configuration::ShortcutAction::OctaveDown))); + focusPtnSc_.setKey(gui_utils::strToKeySeq(shortcuts.at(Configuration::ShortcutAction::FocusOnPattern))); + focusOdrSc_.setKey(gui_utils::strToKeySeq(shortcuts.at(Configuration::ShortcutAction::FocusOnOrder))); + focusInstSc_.setKey(gui_utils::strToKeySeq(shortcuts.at(Configuration::ShortcutAction::FocusOnInstrument))); + playAndStopSc_.setShortcut(gui_utils::strToKeySeq(shortcuts.at(Configuration::ShortcutAction::PlayAndStop))); + ui->actionPlay->setShortcut(gui_utils::strToKeySeq(shortcuts.at(Configuration::ShortcutAction::Play))); + ui->actionPlay_From_Start->setShortcut(gui_utils::strToKeySeq(shortcuts.at(Configuration::ShortcutAction::PlayFromStart))); + ui->actionPlay_Pattern->setShortcut(gui_utils::strToKeySeq(shortcuts.at(Configuration::ShortcutAction::PlayPattern))); + ui->actionPlay_From_Cursor->setShortcut(gui_utils::strToKeySeq(shortcuts.at(Configuration::ShortcutAction::PlayFromCursor))); + ui->actionPlay_From_Marker->setShortcut(gui_utils::strToKeySeq(shortcuts.at(Configuration::ShortcutAction::PlayFromMarker))); + playStepSc_.setShortcut(gui_utils::strToKeySeq(shortcuts.at(Configuration::ShortcutAction::PlayStep))); + ui->actionStop->setShortcut(gui_utils::strToKeySeq(shortcuts.at(Configuration::ShortcutAction::Stop))); + ui->actionEdit_Mode->setShortcut(gui_utils::strToKeySeq(shortcuts.at(Configuration::ShortcutAction::ToggleEditJam))); + ui->actionSet_Ro_w_Marker->setShortcut(gui_utils::strToKeySeq(shortcuts.at(Configuration::ShortcutAction::SetMarker))); + ui->actionMix->setShortcut(gui_utils::strToKeySeq(shortcuts.at(Configuration::ShortcutAction::PasteMix))); + ui->actionOverwrite->setShortcut(gui_utils::strToKeySeq(shortcuts.at(Configuration::ShortcutAction::PasteOverwrite))); + ui->action_Insert->setShortcut(gui_utils::strToKeySeq(shortcuts.at(Configuration::ShortcutAction::PasteInsert))); + ui->actionAll->setShortcut(gui_utils::strToKeySeq(shortcuts.at(Configuration::ShortcutAction::SelectAll))); + ui->actionNone->setShortcut(gui_utils::strToKeySeq(shortcuts.at(Configuration::ShortcutAction::Deselect))); + ui->actionRow->setShortcut(gui_utils::strToKeySeq(shortcuts.at(Configuration::ShortcutAction::SelectRow))); + ui->actionColumn->setShortcut(gui_utils::strToKeySeq(shortcuts.at(Configuration::ShortcutAction::SelectColumn))); + ui->actionPattern->setShortcut(gui_utils::strToKeySeq(shortcuts.at(Configuration::ShortcutAction::SelectColumn))); + ui->actionOrder->setShortcut(gui_utils::strToKeySeq(shortcuts.at(Configuration::ShortcutAction::SelectOrder))); + ui->action_Go_To->setShortcut(gui_utils::strToKeySeq(shortcuts.at(Configuration::ShortcutAction::GoToStep))); + ui->actionToggle_Track->setShortcut(gui_utils::strToKeySeq(shortcuts.at(Configuration::ShortcutAction::ToggleTrack))); + ui->actionSolo_Track->setShortcut(gui_utils::strToKeySeq(shortcuts.at(Configuration::ShortcutAction::SoloTrack))); + ui->actionInterpolate->setShortcut(gui_utils::strToKeySeq(shortcuts.at(Configuration::ShortcutAction::Interpolate))); + goPrevOdrSc_.setShortcut(gui_utils::strToKeySeq(shortcuts.at(Configuration::ShortcutAction::GoToPrevOrder))); + goNextOdrSc_.setShortcut(gui_utils::strToKeySeq(shortcuts.at(Configuration::ShortcutAction::GoToNextOrder))); + ui->action_Toggle_Bookmark->setShortcut(gui_utils::strToKeySeq(shortcuts.at(Configuration::ShortcutAction::ToggleBookmark))); + ui->action_Previous_Bookmark->setShortcut(gui_utils::strToKeySeq(shortcuts.at(Configuration::ShortcutAction::PrevBookmark))); + ui->action_Next_Bookmark->setShortcut(gui_utils::strToKeySeq(shortcuts.at(Configuration::ShortcutAction::NextBookmark))); + ui->actionDecrease_Note->setShortcut(gui_utils::strToKeySeq(shortcuts.at(Configuration::ShortcutAction::DecreaseNote))); + ui->actionIncrease_Note->setShortcut(gui_utils::strToKeySeq(shortcuts.at(Configuration::ShortcutAction::IncreaseNote))); + ui->actionDecrease_Octave->setShortcut(gui_utils::strToKeySeq(shortcuts.at(Configuration::ShortcutAction::DecreaseOctave))); + ui->actionIncrease_Octave->setShortcut(gui_utils::strToKeySeq(shortcuts.at(Configuration::ShortcutAction::IncreaseOctave))); + prevInstSc_.setShortcut(gui_utils::strToKeySeq(shortcuts.at(Configuration::ShortcutAction::PrevInstrument))); + nextInstSc_.setShortcut(gui_utils::strToKeySeq(shortcuts.at(Configuration::ShortcutAction::NextInstrument))); + ui->action_Instrument_Mask->setShortcut(gui_utils::strToKeySeq(shortcuts.at(Configuration::ShortcutAction::MaskInstrument))); + ui->action_Volume_Mask->setShortcut(gui_utils::strToKeySeq(shortcuts.at(Configuration::ShortcutAction::MaskVolume))); + ui->actionEdit->setShortcut(gui_utils::strToKeySeq(shortcuts.at(Configuration::ShortcutAction::EditInstrument))); + ui->actionFollow_Mode->setShortcut(gui_utils::strToKeySeq(shortcuts.at(Configuration::ShortcutAction::FollowMode))); + ui->actionDuplicate_Order->setShortcut(gui_utils::strToKeySeq(shortcuts.at(Configuration::ShortcutAction::DuplicateOrder))); + ui->actionClone_Patterns->setShortcut(gui_utils::strToKeySeq(shortcuts.at(Configuration::ShortcutAction::ClonePatterns))); + ui->actionClone_Order->setShortcut(gui_utils::strToKeySeq(shortcuts.at(Configuration::ShortcutAction::CloneOrder))); + ui->actionReplace_Instrument->setShortcut(gui_utils::strToKeySeq(shortcuts.at(Configuration::ShortcutAction::ReplaceInstrument))); + ui->actionExpand->setShortcut(gui_utils::strToKeySeq(shortcuts.at(Configuration::ShortcutAction::ExpandPattern))); + ui->actionShrink->setShortcut(gui_utils::strToKeySeq(shortcuts.at(Configuration::ShortcutAction::ShrinkPattern))); + ui->actionFine_Decrease_Values->setShortcut(gui_utils::strToKeySeq(shortcuts.at(Configuration::ShortcutAction::FineDecreaseValues))); + ui->actionFine_Increase_Values->setShortcut(gui_utils::strToKeySeq(shortcuts.at(Configuration::ShortcutAction::FineIncreaseValues))); + ui->actionCoarse_D_ecrease_Values->setShortcut(gui_utils::strToKeySeq(shortcuts.at(Configuration::ShortcutAction::CoarseDecreaseValues))); + ui->actionCoarse_I_ncrease_Values->setShortcut(gui_utils::strToKeySeq(shortcuts.at(Configuration::ShortcutAction::CoarseIncreaseValuse))); + incPtnSizeSc_.setShortcut(gui_utils::strToKeySeq(shortcuts.at(Configuration::ShortcutAction::IncreasePatternSize))); + decPtnSizeSc_.setShortcut(gui_utils::strToKeySeq(shortcuts.at(Configuration::ShortcutAction::DecreasePatternSize))); + incEditStepSc_.setShortcut(gui_utils::strToKeySeq(shortcuts.at(Configuration::ShortcutAction::IncreaseEditStep))); + decEditStepSc_.setShortcut(gui_utils::strToKeySeq(shortcuts.at(Configuration::ShortcutAction::DecreaseEditStep))); + ui->action_Effect_List->setShortcut(gui_utils::strToKeySeq(shortcuts.at(Configuration::ShortcutAction::DisplayEffectList))); + prevSongSc_.setShortcut(gui_utils::strToKeySeq(shortcuts.at(Configuration::ShortcutAction::PreviousSong))); + nextSongSc_.setShortcut(gui_utils::strToKeySeq(shortcuts.at(Configuration::ShortcutAction::NextSong))); + jamVolUpSc_.setShortcut(gui_utils::strToKeySeq(shortcuts.at(Configuration::ShortcutAction::JamVolumeUp))); + jamVolDownSc_.setShortcut(gui_utils::strToKeySeq(shortcuts.at(Configuration::ShortcutAction::JamVolumeDown))); ui->orderList->onShortcutUpdated(); ui->patternEditor->onShortcutUpdated(); @@ -1689,16 +1697,17 @@ } try { - QFile fp(file); - if (!fp.open(QIODevice::ReadOnly)) { - FileIOErrorMessageBox::openError(file, true, io::FileType::Inst, this); - return; - } - QByteArray array = fp.readAll(); - fp.close(); - io::BinaryContainer contaner; - contaner.appendVector(std::vector(array.begin(), array.end())); + { + QFile fp(file); + if (!fp.open(QIODevice::ReadOnly)) { + FileIOErrorMessageBox::openError(file, true, io::FileType::Inst, this); + return; + } + QByteArray&& array = fp.readAll(); + fp.close(); + std::move(array.begin(), array.end(), std::back_inserter(contaner)); + } bt_->loadInstrument(contaner, file.toStdString(), n); auto inst = bt_->getInstrument(n); @@ -1735,15 +1744,19 @@ if (!file.endsWith(".bti")) file += ".bti"; // For linux try { - io::BinaryContainer container; - bt_->saveInstrument(container, n); - + QByteArray bytes; + { + io::BinaryContainer container; + bt_->saveInstrument(container, n); + bytes.reserve(container.size()); + std::move(container.begin(), container.end(), std::back_inserter(bytes)); + } QFile fp(file); if (!fp.open(QIODevice::WriteOnly)) { FileIOErrorMessageBox::openError(file, false, io::FileType::Inst, this); return; } - fp.write(container.getPointer(), container.size()); + fp.write(bytes); fp.close(); config_.lock()->setWorkingDirectory(QFileInfo(file).dir().path().toStdString()); @@ -1784,18 +1797,18 @@ stopPlaySong(); std::unique_ptr bank; - auto bankMan = std::make_shared(true); try { - QFile fp(file); - if (!fp.open(QIODevice::ReadOnly)) { - FileIOErrorMessageBox::openError(file, true, io::FileType::Bank, this); - return; - } - QByteArray array = fp.readAll(); - fp.close(); - io::BinaryContainer container; - container.appendVector(std::vector(array.begin(), array.end())); + { + QFile fp(file); + if (!fp.open(QIODevice::ReadOnly)) { + FileIOErrorMessageBox::openError(file, true, io::FileType::Bank, this); + return; + } + QByteArray&& array = fp.readAll(); + fp.close(); + std::move(array.begin(), array.end(), std::back_inserter(container)); + } bank.reset(io::BankIO::getInstance().loadBank(container, file.toStdString())); config_.lock()->setWorkingDirectory(QFileInfo(file).dir().path().toStdString()); @@ -1830,6 +1843,7 @@ size_t jamId = 128; // Dummy std::shared_ptr jamInst; importBankDiag_ = std::make_unique(*bank, tr("Select instruments to load:"), config_, this); + auto bankMan = std::make_shared(true); auto updateInst = [&] (size_t id) { if (id != jamId) { bankJamMidiCtrl_.store(true); @@ -1837,10 +1851,11 @@ bankMan->clearAll(); jamInst.reset(bank->loadInstrument(id, bankMan, 0)); jamInst->setNumber(128); // Special number - std::vector> addrs = bt_->assignADPCMBeforeForcedJamKeyOn(jamInst); - for (size_t i = 0; i < addrs.size(); ++i) { - bankMan->setSampleADPCMStartAddress(i, addrs[i][0]); - bankMan->setSampleADPCMStopAddress(i, addrs[i][1]); + std::unordered_map> sampNums; + bt_->assignADPCMBeforeForcedJamKeyOn(jamInst, sampNums); + for (const auto& pairs : sampNums) { + bankMan->setSampleADPCMStartAddress(pairs.first, pairs.second[0]); + bankMan->setSampleADPCMStopAddress(pairs.first, pairs.second[1]); } bankJamMidiCtrl_.store(false); } @@ -1873,7 +1888,7 @@ return; } - QVector selection = importBankDiag_->currentInstrumentSelection(); + const QVector selection = importBankDiag_->currentInstrumentSelection(); importBankDiag_.reset(); if (selection.empty()) return; @@ -1939,15 +1954,18 @@ std::sort(sel.begin(), sel.end()); try { - io::BinaryContainer container; - bt_->exportInstruments(container, sel); - + QByteArray bytes; + { + io::BinaryContainer container; + bt_->exportInstruments(container, sel); + bytes.reserve(container.size()); + std::move(container.begin(), container.end(), std::back_inserter(bytes)); + } QFile fp(file); if (!fp.open(QIODevice::WriteOnly)) { FileIOErrorMessageBox::openError(file, false, io::FileType::Bank, this); return; - } - fp.write(container.getPointer(), container.size()); + }fp.write(bytes); fp.close(); config_.lock()->setWorkingDirectory(QFileInfo(file).dir().path().toStdString()); @@ -2082,14 +2100,15 @@ if (tickTimerForRealChip_) tickTimerForRealChip_->stop(); else stream_->stop(); - io::BinaryContainer container; QFile fp(file); if (fp.open(QIODevice::ReadOnly)) { + io::BinaryContainer container; + { + QByteArray&& array = fp.readAll(); + fp.close(); + std::move(array.begin(), array.end(), std::back_inserter(container)); + } - QByteArray array = fp.readAll(); - fp.close(); - - container.appendVector(std::vector(array.begin(), array.end())); bt_->loadModule(container); bt_->setModulePath(file.toStdString()); @@ -2098,7 +2117,7 @@ config_.lock()->setWorkingDirectory(QFileInfo(file).dir().path().toStdString()); changeFileHistory(file); - goto AFTER_LOADING; // Skip error handling section + goto AFTER_MOD_LOADING; // Skip error handling section } else { FileIOErrorMessageBox::openError(file, true, io::FileType::Mod, this); @@ -2118,7 +2137,7 @@ bt_->makeNewModule(); loadModule(); -AFTER_LOADING: // Post process of module loading +AFTER_MOD_LOADING: // Post process of module loading isModifiedForNotCommand_ = false; setWindowModified(false); if (tickTimerForRealChip_) tickTimerForRealChip_->start(); @@ -2182,8 +2201,14 @@ lockWidgets(false); if (tickTimerForRealChip_) tickTimerForRealChip_->stop(); else stream_->stop(); - bt_->assignSampleADPCMRawSamples(); // Mutex register + bool isStoredAll = bt_->assignSampleADPCMRawSamples(); // Mutex register instForms_->onInstrumentADPCMSampleMemoryUpdated(); + + if (!isStoredAll) { + QMessageBox::warning(this, tr("Warning"), + tr("Insufficient memory size to load ADPCM samples. Please delete the unused samples.")); + } + if (tickTimerForRealChip_) tickTimerForRealChip_->start(); else stream_->start(); } @@ -2924,8 +2949,8 @@ tr("About"), QString("

BambooTracker v%1

").arg( QString::fromStdString(Version::ofApplicationInString())) - + tr("YM2608 (OPNA) Music Tracker
" - "Copyright (C) 2018-2020 Rerrah

" + + tr("YM2608 Music Tracker
" + "Copyright (C) 2018-2021 Rerrah

" "
" "Libraries:
" "- C86CTL by (C) honet (BSD 3-Clause)
" @@ -2979,7 +3004,7 @@ if (diag.exec() == QDialog::Accepted) { changeConfiguration(); - ConfigurationHandler::saveConfiguration(config_.lock()); + io::saveConfiguration(config_.lock()); ColorPaletteHandler::savePalette(palette_.get()); } } @@ -3078,15 +3103,19 @@ if (!backupModule(path)) return false; try { - io::BinaryContainer container; - bt_->saveModule(container); - + QByteArray bytes; + { + io::BinaryContainer container; + bt_->saveModule(container); + bytes.reserve(container.size()); + std::move(container.begin(), container.end(), std::back_inserter(bytes)); + } QFile fp(path); if (!fp.open(QIODevice::WriteOnly)) { FileIOErrorMessageBox::openError(path, false, io::FileType::Mod, this); return false; } - fp.write(container.getPointer(), container.size()); + fp.write(bytes); fp.close(); isModifiedForNotCommand_ = false; @@ -3125,15 +3154,20 @@ bt_->setModulePath(file.toStdString()); try { - io::BinaryContainer container; - bt_->saveModule(container); + QByteArray bytes; + { + io::BinaryContainer container; + bt_->saveModule(container); + bytes.reserve(container.size()); + std::move(container.begin(), container.end(), std::back_inserter(bytes)); + } QFile fp(file); if (!fp.open(QIODevice::WriteOnly)) { FileIOErrorMessageBox::openError(file, false, io::FileType::Mod, this); return false; } - fp.write(container.getPointer(), container.size()); + fp.write(bytes); fp.close(); isModifiedForNotCommand_ = false; @@ -3288,7 +3322,7 @@ if (path.isNull()) return; if (!path.endsWith(".wav")) path += ".wav"; // For linux - int max = static_cast(bt_->getAllStepCount( + int max = static_cast(bt_->getTotalStepCount( bt_->getCurrentSongNumber(), static_cast(diag.getLoopCount()))) + 3; QProgressDialog progress(tr("Export to WAV"), tr("Cancel"), 0, max); progress.setValue(0); @@ -3302,33 +3336,34 @@ stream_->stop(); try { - const uint32_t rate = static_cast(diag.getSampleRate()); - const uint16_t nCh = 2; - const int loopCnt = diag.getLoopCount(); - size_t defCap = static_cast(rate * nCh * loopCnt - * bt_->calculateSongLength(bt_->getCurrentSongNumber())); - io::WavContainer container(defCap, rate, nCh, 16); auto bar = [&progress]() -> bool { QApplication::processEvents(); progress.setValue(progress.value() + 1); return progress.wasCanceled(); }; - bool res = bt_->exportToWav(container, loopCnt, bar); - if (res) { - QFile fp(path); - if (!fp.open(QIODevice::WriteOnly)) { - FileIOErrorMessageBox::openError(path, false, io::FileType::WAV, this); - } - else { - io::BinaryContainer bc = container.createWavBinary(); - fp.write(bc.getPointer(), bc.size()); - fp.close(); - bar(); + QByteArray bytes; + { + const uint32_t rate = static_cast(diag.getSampleRate()); + const uint16_t nCh = 2; + const int loopCnt = diag.getLoopCount(); + io::WavContainer container(rate, nCh, 16); + if (!bt_->exportToWav(container, loopCnt, bar)) + goto AFTER_WAV_WRITE; // Jump if cancelled + bytes.reserve(container.size()); + std::move(container.begin(), container.end(), std::back_inserter(bytes)); + } - config_.lock()->setWorkingDirectory(QFileInfo(path).dir().path().toStdString()); - } + QFile fp(path); + if (!fp.open(QIODevice::WriteOnly)) { + FileIOErrorMessageBox::openError(path, false, io::FileType::WAV, this); + goto AFTER_WAV_WRITE; // Jump to post process } + fp.write(bytes); + fp.close(); + bar(); + + config_.lock()->setWorkingDirectory(QFileInfo(path).dir().path().toStdString()); } catch (io::FileIOError& e) { FileIOErrorMessageBox(path, false, e, this).exec(); @@ -3337,6 +3372,7 @@ FileIOErrorMessageBox(path, false, io::FileType::WAV, QString(e.what()), this).exec(); } +AFTER_WAV_WRITE: stream_->start(); } @@ -3354,7 +3390,7 @@ if (path.isNull()) return; if (!path.endsWith(".vgm")) path += ".vgm"; // For linux - int max = static_cast(bt_->getAllStepCount(bt_->getCurrentSongNumber(), 1)) + 3; + int max = static_cast(bt_->getTotalStepCount(bt_->getCurrentSongNumber(), 1)) + 3; QProgressDialog progress(tr("Export to VGM"), tr("Cancel"), 0, max); progress.setValue(0); progress.setWindowFlags(progress.windowFlags() @@ -3367,27 +3403,30 @@ stream_->stop(); try { - io::BinaryContainer container; auto bar = [&progress]() -> bool { QApplication::processEvents(); progress.setValue(progress.value() + 1); return progress.wasCanceled(); }; - bool res = bt_->exportToVgm(container, diag.getExportTarget(), diag.enabledGD3(), tag, bar); - if (res) { - QFile fp(path); - if (!fp.open(QIODevice::WriteOnly)) { - FileIOErrorMessageBox::openError(path, false, io::FileType::VGM, this); - } - else { - fp.write(container.getPointer(), container.size()); - fp.close(); - bar(); - - config_.lock()->setWorkingDirectory(QFileInfo(path).dir().path().toStdString()); - } + QByteArray bytes; + { + io::BinaryContainer container; + if (!bt_->exportToVgm(container, diag.getExportTarget(), diag.enabledGD3(), tag, bar)) + goto AFTER_VGM_WRITE; // Jump if cancelled + bytes.reserve(container.size()); + std::move(container.begin(), container.end(), std::back_inserter(bytes)); + } + QFile fp(path); + if (!fp.open(QIODevice::WriteOnly)) { + FileIOErrorMessageBox::openError(path, false, io::FileType::VGM, this); + goto AFTER_VGM_WRITE; // Jump if cancelled } + fp.write(bytes); + fp.close(); + bar(); + + config_.lock()->setWorkingDirectory(QFileInfo(path).dir().path().toStdString()); } catch (io::FileIOError& e) { FileIOErrorMessageBox(path, false, e, this).exec(); @@ -3396,6 +3435,7 @@ FileIOErrorMessageBox(path, false, io::FileType::VGM, QString(e.what()), this).exec(); } +AFTER_VGM_WRITE: stream_->start(); } @@ -3413,7 +3453,7 @@ if (path.isNull()) return; if (!path.endsWith(".s98")) path += ".s98"; // For linux - int max = static_cast(bt_->getAllStepCount(bt_->getCurrentSongNumber(), 1)) + 3; + int max = static_cast(bt_->getTotalStepCount(bt_->getCurrentSongNumber(), 1)) + 3; QProgressDialog progress(tr("Export to S98"), tr("Cancel"), 0, max); progress.setValue(0); progress.setWindowFlags(progress.windowFlags() @@ -3426,28 +3466,32 @@ stream_->stop(); try { - io::BinaryContainer container; auto bar = [&progress]() -> bool { QApplication::processEvents(); progress.setValue(progress.value() + 1); return progress.wasCanceled(); }; - bool res = bt_->exportToS98(container, diag.getExportTarget(), diag.enabledTag(), - tag, diag.getResolution(), bar); - if (res) { - QFile fp(path); - if (!fp.open(QIODevice::WriteOnly)) { - FileIOErrorMessageBox::openError(path, false, io::FileType::S98, this); - } - else { - fp.write(container.getPointer(), container.size()); - fp.close(); - bar(); + QByteArray bytes; + { + io::BinaryContainer container; + if (!bt_->exportToS98(container, diag.getExportTarget(), diag.enabledTag(), + tag, diag.getResolution(), bar)) + goto AFTER_S98_WRITE; // Jump if cancelled + bytes.reserve(container.size()); + std::move(container.begin(), container.end(), std::back_inserter(bytes)); + } - config_.lock()->setWorkingDirectory(QFileInfo(path).dir().path().toStdString()); - } + QFile fp(path); + if (!fp.open(QIODevice::WriteOnly)) { + FileIOErrorMessageBox::openError(path, false, io::FileType::S98, this); + goto AFTER_S98_WRITE; // Jump to post process } + fp.write(bytes); + fp.close(); + bar(); + + config_.lock()->setWorkingDirectory(QFileInfo(path).dir().path().toStdString()); } catch (io::FileIOError& e) { FileIOErrorMessageBox(path, false, e, this).exec(); @@ -3456,6 +3500,7 @@ FileIOErrorMessageBox(path, false, io::FileType::S98, QString(e.what()), this).exec(); } +AFTER_S98_WRITE: stream_->start(); } @@ -3529,10 +3574,10 @@ void MainWindow::updateVisuals() { - int16_t wave[2 * OPNAController::OUTPUT_HISTORY_SIZE]; + int16_t wave[2 * bt_defs::OUTPUT_HISTORY_SIZE]; bt_->getOutputHistory(wave); - ui->waveVisual->setStereoSamples(wave, OPNAController::OUTPUT_HISTORY_SIZE); + ui->waveVisual->setStereoSamples(wave, bt_defs::OUTPUT_HISTORY_SIZE); } void MainWindow::on_action_Effect_List_triggered() @@ -3570,17 +3615,14 @@ bt_->stopPlaySong(); lockWidgets(false); - std::vector> duplicates = bt_->checkDuplicateInstruments(); + std::unordered_map rplMap = bt_->replaceDuplicateInstrumentsInPatterns(); auto list = ui->instrumentList; - for (auto& group : duplicates) { - for (size_t i = 1; i < group.size(); ++i) { - for (int j = 0; j < list->count(); ++j) { - if (list->item(j)->data(Qt::UserRole).toInt() == group[i]) - removeInstrument(j); - } + for (auto& pairs : rplMap) { + for (int j = 0; j < list->count(); ++j) { + if (list->item(j)->data(Qt::UserRole).toInt() == pairs.first) + removeInstrument(j); } } - bt_->replaceDuplicateInstrumentsInPatterns(duplicates); bt_->clearUnusedInstrumentProperties(); bt_->clearCommandHistory(); comStack_->clear(); @@ -3767,7 +3809,7 @@ void MainWindow::on_action_Estimate_Song_Length_triggered() { - double time = bt_->calculateSongLength(bt_->getCurrentSongNumber()); + double time = bt_->getApproximateSongLength(bt_->getCurrentSongNumber()); int seconds = static_cast(std::round(time)); QMessageBox box; box.setIcon(QMessageBox::Information); diff -Nru bambootracker-0.4.5+git20121209/BambooTracker/gui/mainwindow.hpp bambootracker-0.4.6/BambooTracker/gui/mainwindow.hpp --- bambootracker-0.4.5+git20121209/BambooTracker/gui/mainwindow.hpp 2020-12-08 14:05:28.000000000 +0000 +++ bambootracker-0.4.6/BambooTracker/gui/mainwindow.hpp 2021-02-11 14:37:17.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (C) 2018-2020 Rerrah + * Copyright (C) 2018-2021 Rerrah * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation @@ -29,7 +29,6 @@ #include #include #include -#include #include #include #include @@ -186,13 +185,6 @@ // Toolbar QSpinBox *octave_, *highlight1_, *highlight2_, *volume_; - using ToolbarPos = Configuration::ToolbarConfiguration::ToolbarPosition; - const std::unordered_map TB_POS_ = { - {ToolbarPos::TOP_POS, Qt::TopToolBarArea }, - { ToolbarPos::BOTTOM_POS, Qt::BottomToolBarArea }, - { ToolbarPos::LEFT_POS, Qt::LeftToolBarArea }, - { ToolbarPos::RIGHT_POS, Qt::RightToolBarArea } - }; // Status bars QLabel *statusDetail_, *statusStyle_, *statusInst_, *statusOctave_; diff -Nru bambootracker-0.4.5+git20121209/BambooTracker/gui/module_properties_dialog.hpp bambootracker-0.4.6/BambooTracker/gui/module_properties_dialog.hpp --- bambootracker-0.4.5+git20121209/BambooTracker/gui/module_properties_dialog.hpp 2020-12-08 14:05:28.000000000 +0000 +++ bambootracker-0.4.6/BambooTracker/gui/module_properties_dialog.hpp 2021-02-11 14:37:17.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (C) 2018-2020 Rerrah + * Copyright (C) 2018-2021 Rerrah * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation @@ -32,7 +32,6 @@ #include #include #include "bamboo_tracker.hpp" -#include "misc.hpp" #include "enum_hash.hpp" namespace Ui { diff -Nru bambootracker-0.4.5+git20121209/BambooTracker/gui/order_list_editor/order_list_panel.cpp bambootracker-0.4.6/BambooTracker/gui/order_list_editor/order_list_panel.cpp --- bambootracker-0.4.5+git20121209/BambooTracker/gui/order_list_editor/order_list_panel.cpp 2020-12-08 14:05:28.000000000 +0000 +++ bambootracker-0.4.6/BambooTracker/gui/order_list_editor/order_list_panel.cpp 2021-02-11 14:37:17.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (C) 2018-2020 Rerrah + * Copyright (C) 2018-2021 Rerrah * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation @@ -36,16 +36,16 @@ #include #include #include -#include -#include #include #include #include -#include "gui/event_guard.hpp" -#include "gui/command/order/order_commands.hpp" #include "playback.hpp" #include "track.hpp" +#include "bamboo_tracker_defs.hpp" +#include "gui/event_guard.hpp" +#include "gui/command/order/order_commands_qt.hpp" #include "gui/gui_utils.hpp" +#include "utils.hpp" OrderListPanel::OrderListPanel(QWidget *parent) : QWidget(parent), @@ -331,7 +331,7 @@ QPainter backPainter(&backPixmap_); textPainter.setFont(rowFont_); - std::vector orderRowData_; + std::vector orderRowData_; int textOffset = trackWidth_ / 2 - rowFontWidth_; /* Current row */ @@ -486,7 +486,7 @@ QPainter backPainter(&backPixmap_); textPainter.setFont(rowFont_); - std::vector orderRowData_; + std::vector orderRowData_; int textOffset = trackWidth_ / 2 - rowFontWidth_; /* Clear previous cursor row, current cursor row and last rows text */ @@ -642,21 +642,11 @@ break; case SongType::FM3chExpanded: switch (attrib.channelInSource) { - case 2: - str = "OP1"; - break; - case 6: - str = "OP2"; - break; - case 7: - str = "OP3"; - break; - case 8: - str = "OP4"; - break; - default: - str = "FM" + QString::number(attrib.channelInSource + 1); - break; + case 2: str = "OP1"; break; + case 6: str = "OP2"; break; + case 7: str = "OP3"; break; + case 8: str = "OP4"; break; + default: str = "FM" + QString::number(attrib.channelInSource + 1); break; } break; } @@ -665,28 +655,18 @@ str = "SG" + QString::number(attrib.channelInSource + 1); break; case SoundSource::RHYTHM: - switch (attrib.channelInSource) { - case 0: str = "BD"; break; - case 1: str = "SD"; break; - case 2: str = "TOP"; break; - case 3: str = "HH"; break; - case 4: str = "TOM"; break; - case 5: str = "RIM"; break; - } + static const QString RHYTM_NAMES[6] = { + "BD", "SD", "TOP", "HH", "TOM", "RIM" + }; + str = RHYTM_NAMES[attrib.channelInSource]; break; case SoundSource::ADPCM: str = "AP"; break; } -#if QT_VERSION >= QT_VERSION_CHECK(5, 11, 0) - int rw = trackWidth_ - hdFontMets_->horizontalAdvance(str); -#else - int rw = trackWidth_ - hdFontMets_->width(str); -#endif - rw = (rw < 0) ? 0 : (rw / 2); painter.setPen(palette_->odrHeaderTextColor); - painter.drawText(x + rw, headerFontAscent_, str); + painter.drawText(QRectF(x, 0, trackWidth_, headerFontAscent_), Qt::AlignCenter, str); x += trackWidth_; } @@ -851,7 +831,7 @@ } if (trackChanged) { // Update horizontal position - int trackVisIdx = std::distance(visTracks_.begin(), std::find(visTracks_.begin(), visTracks_.end(), bt_->getCurrentTrackAttribute().number)); + int trackVisIdx = std::distance(visTracks_.begin(), utils::find(visTracks_, bt_->getCurrentTrackAttribute().number)); int prevTrackIdx = std::exchange(curPos_.trackVisIdx, trackVisIdx); if (prevTrackIdx < curPos_.trackVisIdx) { while (calculateColumnsWidthWithRowNum(leftTrackVisIdx_, curPos_.trackVisIdx) > geometry().width()) { @@ -999,7 +979,7 @@ QString str = QString("ORDER_COPY:%1,%2,") .arg(QString::number(w), QString::number(h)); for (int i = 0; i < h; ++i) { - std::vector odrs = bt_->getOrderData(curSongNum_, selLeftAbovePos_.row + i); + std::vector odrs = bt_->getOrderData(curSongNum_, selLeftAbovePos_.row + i); for (int j = 0; j < w; ++j) { str += QString::number(odrs.at(static_cast(visTracks_.at(selLeftAbovePos_.trackVisIdx) + j)).patten); if (i < h - 1 || j < w - 1) str += ","; @@ -1012,28 +992,18 @@ void OrderListPanel::pasteCopiedCells(const OrderPosition& startPos) { // Analyze text - QString str = QApplication::clipboard()->text().remove(QRegularExpression("ORDER_COPY:")); - QString hdRe = "^([0-9]+),([0-9]+),"; - QRegularExpression re(hdRe); - QRegularExpressionMatch match = re.match(str); - int w = match.captured(1).toInt(); - size_t h = match.captured(2).toUInt(); - str.remove(re); + QString str = QApplication::clipboard()->text().remove("ORDER_COPY:"); + QStringList data = str.split(","); + if (data.size() < 2) return; + size_t w = data[0].toUInt(); + size_t h = data[1].toUInt(); + data.erase(data.begin(), data.begin() + 2); + if (static_cast(w * h) != data.size()) return; - std::vector> cells; - re = QRegularExpression("^([^,]+),"); + std::vector> cells(h, std::vector(w)); for (size_t i = 0; i < h; ++i) { - cells.emplace_back(); - for (int j = 0; j < w; ++j) { - match = re.match(str); - if (match.hasMatch()) { - cells.at(i).push_back(match.captured(1).toStdString()); - str.remove(re); - } - else { - cells.at(i).push_back(str.toStdString()); - break; - } + for (size_t j = 0; j < w; ++j) { + cells[i][j] = data[i * w + j].toStdString(); } } @@ -1115,9 +1085,9 @@ paste->setShortcutVisibleInContextMenu(true); #endif auto shortcuts = config_->getShortcuts(); - duplicate->setShortcut(gui_utils::strToKeySeq(shortcuts.at(Configuration::DuplicateOrder))); - clonep->setShortcut(gui_utils::strToKeySeq(shortcuts.at(Configuration::ClonePatterns))); - cloneo->setShortcut(gui_utils::strToKeySeq(shortcuts.at(Configuration::CloneOrder))); + duplicate->setShortcut(gui_utils::strToKeySeq(shortcuts.at(Configuration::ShortcutAction::DuplicateOrder))); + clonep->setShortcut(gui_utils::strToKeySeq(shortcuts.at(Configuration::ShortcutAction::ClonePatterns))); + cloneo->setShortcut(gui_utils::strToKeySeq(shortcuts.at(Configuration::ShortcutAction::CloneOrder))); copy->setShortcut(QKeySequence(Qt::CTRL + Qt::Key_C)); paste->setShortcut(QKeySequence(Qt::CTRL + Qt::Key_V)); @@ -1453,11 +1423,14 @@ return true; } default: - if (event->modifiers().testFlag(Qt::NoModifier)) { + { + auto modifiers = event->modifiers(); + if (modifiers.testFlag(Qt::NoModifier) || modifiers.testFlag(Qt::KeypadModifier)) { return enterOrder(event->key()); } return false; } + } } bool OrderListPanel::keyReleased(QKeyEvent* event) diff -Nru bambootracker-0.4.5+git20121209/BambooTracker/gui/pattern_editor/pattern_editor_panel.cpp bambootracker-0.4.6/BambooTracker/gui/pattern_editor/pattern_editor_panel.cpp --- bambootracker-0.4.5+git20121209/BambooTracker/gui/pattern_editor/pattern_editor_panel.cpp 2020-12-08 14:05:28.000000000 +0000 +++ bambootracker-0.4.6/BambooTracker/gui/pattern_editor/pattern_editor_panel.cpp 2021-02-11 14:37:17.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (C) 2018-2020 Rerrah + * Copyright (C) 2018-2021 Rerrah * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation @@ -40,16 +40,19 @@ #include #include #include -#include #include #include -#include "gui/event_guard.hpp" -#include "gui/command/pattern/pattern_commands_qt.hpp" #include "midi/midi.hpp" +#include "command/pattern/set_effect_value_to_step_command.hpp" #include "jamming.hpp" +#include "note.hpp" +#include "bamboo_tracker_defs.hpp" +#include "gui/event_guard.hpp" +#include "gui/command/pattern/pattern_commands_qt.hpp" #include "gui/effect_description.hpp" #include "gui/jam_layout.hpp" #include "gui/gui_utils.hpp" +#include "utils.hpp" PatternEditorPanel::PatternEditorPanel(QWidget *parent) : QWidget(parent), @@ -986,10 +989,10 @@ else { int volLim = 0; // Dummy set switch (src) { - case SoundSource::FM: volLim = 0x80; break; - case SoundSource::SSG: volLim = 0x10; break; - case SoundSource::RHYTHM: volLim = 0x20; break; - case SoundSource::ADPCM: volLim = 0x100; break; + case SoundSource::FM: volLim = bt_defs::NSTEP_FM_VOLUME ; break; + case SoundSource::SSG: volLim = bt_defs::NSTEP_SSG_VOLUME; break; + case SoundSource::RHYTHM: volLim = bt_defs::NSTEP_RHYTHM_VOLUME; break; + case SoundSource::ADPCM: volLim = bt_defs::NSTEP_ADPCM_VOLUME; break; } textPainter.setPen((vol < volLim) ? palette_->ptnVolColor : palette_->ptnErrorColor); if (src == SoundSource::FM && vol < volLim && config_->getReverseFMVolumeOrder()) { @@ -1044,14 +1047,14 @@ } else { textPainter.setPen(palette_->ptnEffColor); - switch (Effect::toEffectType(src, effId)) { + switch (effect_utils::validateEffectId(src, effId)) { case EffectType::VolumeDelay: - if (src == SoundSource::FM && config_->getReverseFMVolumeOrder() && effVal < 0x80) - effVal = 0x7f - effVal; + if (src == SoundSource::FM && config_->getReverseFMVolumeOrder()) + effVal = effect_utils::reverseFmVolume(effVal); break; case EffectType::Brightness: - if (config_->getReverseFMVolumeOrder() && effVal > 0) - effVal = 0xff - effVal + 1; + if (config_->getReverseFMVolumeOrder()) + effVal = effect_utils::reverseFmBrightness(effVal); break; default: break; @@ -1458,7 +1461,7 @@ } if (trackChanged) { - int trackVisIdx = std::distance(visTracks_.begin(), std::find(visTracks_.begin(), visTracks_.end(), bt_->getCurrentTrackAttribute().number)); + int trackVisIdx = std::distance(visTracks_.begin(), utils::find(visTracks_, bt_->getCurrentTrackAttribute().number)); int oldTrackVisIdx = std::exchange(curPos_.trackVisIdx, trackVisIdx); curPos_.colInTrack = 0; if (oldTrackVisIdx < curPos_.trackVisIdx) { @@ -1521,52 +1524,24 @@ { int baseOct = bt_->getCurrentOctave(); - if (event->modifiers().testFlag(Qt::NoModifier)) { + auto modifiers = event->modifiers(); + if (modifiers.testFlag(Qt::NoModifier) || modifiers.testFlag(Qt::KeypadModifier)) { Qt::Key qtKey = static_cast(event->key()); try { JamKey possibleJamKey = getJamKeyFromLayoutMapping(qtKey, config_); - int octaveOffset = 0; - switch (possibleJamKey) { - case JamKey::HighD2: - case JamKey::HighCS2: - case JamKey::HighC2: - octaveOffset = 2; - break; - case JamKey::HighB: - case JamKey::HighAS: - case JamKey::HighA: - case JamKey::HighGS: - case JamKey::HighG: - case JamKey::HighFS: - case JamKey::HighF: - case JamKey::HighE: - case JamKey::HighDS: - case JamKey::HighD: - case JamKey::HighCS: - case JamKey::HighC: - case JamKey::LowD2: - case JamKey::LowCS2: - case JamKey::LowC2: - octaveOffset = 1; - break; - default: - break; - } - setStepKeyOn(jam_utils::jamKeyToNote(possibleJamKey), baseOct + octaveOffset); + setStepKeyOn(jam_utils::makeNote(baseOct, possibleJamKey)); } catch (std::invalid_argument &) {} } return false; } -void PatternEditorPanel::setStepKeyOn(Note note, int octave) +void PatternEditorPanel::setStepKeyOn(const Note& note) { - if (octave < 8) { - bt_->setStepNote(curSongNum_, visTracks_.at(curPos_.trackVisIdx), curPos_.order, curPos_.step, octave, note, - config_->getInstrumentMask(), config_->getVolumeMask()); - comStack_.lock()->push(new SetKeyOnToStepQtCommand(this)); - if (!bt_->isPlaySong() || !bt_->isFollowPlay()) moveCursorToDown(editableStepCnt_); - } + bt_->setStepNote(curSongNum_, visTracks_.at(curPos_.trackVisIdx), curPos_.order, curPos_.step, note, + config_->getInstrumentMask(), config_->getVolumeMask()); + comStack_.lock()->push(new SetKeyOnToStepQtCommand(this)); + if (!bt_->isPlaySong() || !bt_->isFollowPlay()) moveCursorToDown(editableStepCnt_); } bool PatternEditorPanel::enterInstrumentData(int key) @@ -1701,7 +1676,7 @@ std::string id = bt_->getStepEffectID(curSongNum_, visTracks_.at(editPos.trackVisIdx), editPos.order, editPos.step, (editPos.colInTrack - 3) / 2); SoundSource src = songStyle_.trackAttribs.at(static_cast(curTrackNum)).source; - emit effectEntered(EffectDescription::getEffectFormatAndDetailString(Effect::toEffectType(src, id))); + emit effectEntered(EffectDescription::getEffectFormatAndDetailString(effect_utils::validateEffectId(src, id))); } bool PatternEditorPanel::enterEffectValue(int key) @@ -1733,7 +1708,7 @@ int n = (curPos_.colInTrack - 4) / 2; EffectDisplayControl ctrl = EffectDisplayControl::Unset; SoundSource src = songStyle_.trackAttribs[static_cast(trackNum)].source; - switch (Effect::toEffectType( + switch (effect_utils::validateEffectId( src, bt_->getStepEffectID(curSongNum_, trackNum, curPos_.order, curPos_.step, n))) { case EffectType::VolumeDelay: @@ -1860,7 +1835,7 @@ int sCol = 0; PatternCells cells = decodeCells(QApplication::clipboard()->text(), sCol); PatternPosition pos = getPasteLeftAbovePosition(sCol, cursorPos, cells.front().size()); - if (config_->getPasteMode() == Configuration::FILL && selLeftAbovePos_.order != -1) { + if (config_->getPasteMode() == Configuration::PasteMode::Fill && selLeftAbovePos_.order != -1) { cells = compandPasteCells(pos, cells); } @@ -1874,7 +1849,7 @@ int sCol = 0; PatternCells cells = decodeCells(QApplication::clipboard()->text(), sCol); PatternPosition pos = getPasteLeftAbovePosition(sCol, cursorPos, cells.front().size()); - if (config_->getPasteMode() == Configuration::FILL && selLeftAbovePos_.order != -1) { + if (config_->getPasteMode() == Configuration::PasteMode::Fill && selLeftAbovePos_.order != -1) { cells = compandPasteCells(pos, cells); } @@ -1888,7 +1863,7 @@ int sCol = 0; PatternCells cells = decodeCells(QApplication::clipboard()->text(), sCol); PatternPosition pos = getPasteLeftAbovePosition(sCol, cursorPos, cells.front().size()); - if (config_->getPasteMode() == Configuration::FILL && selLeftAbovePos_.order != -1) { + if (config_->getPasteMode() == Configuration::PasteMode::Fill && selLeftAbovePos_.order != -1) { cells = compandPasteCells(pos, cells); } @@ -1902,7 +1877,7 @@ int sCol = 0; PatternCells cells = decodeCells(QApplication::clipboard()->text(), sCol); PatternPosition pos = getPasteLeftAbovePosition(sCol, cursorPos, cells.front().size()); - if (config_->getPasteMode() == Configuration::FILL && selLeftAbovePos_.order != -1) { + if (config_->getPasteMode() == Configuration::PasteMode::Fill && selLeftAbovePos_.order != -1) { cells = compandPasteCells(pos, cells); } @@ -1914,28 +1889,18 @@ PatternEditorPanel::PatternCells PatternEditorPanel::decodeCells(QString str, int& startCol) { str.remove(QRegularExpression("PATTERN_(COPY|CUT):")); - QString hdRe = "^([0-9]+),([0-9]+),([0-9]+),"; - QRegularExpression re(hdRe); - QRegularExpressionMatch match = re.match(str); - startCol = match.captured(1).toInt(); - int w = match.captured(2).toInt(); - size_t h = match.captured(3).toUInt(); - str.remove(re); + QStringList data = str.split(","); + if (data.size() < 3) {}; // Error + startCol = data[0].toInt(); + size_t w = data[1].toUInt(); + size_t h = data[2].toUInt(); + data.erase(data.begin(), data.begin() + 3); + if (static_cast(w * h) != data.size()) return {}; // Error - std::vector> cells; - re = QRegularExpression("^([^,]+),"); + PatternCells cells(h, PatternCells::value_type(w)); for (size_t i = 0; i < h; ++i) { - cells.emplace_back(); - for (int j = 0; j < w; ++j) { - match = re.match(str); - if (match.hasMatch()) { - cells.at(i).push_back(match.captured(1).toStdString()); - str.remove(re); - } - else { - cells.at(i).push_back(str.toStdString()); - break; - } + for (size_t j = 0; j < w; ++j) { + cells[i][j] = data[i * w + j].toStdString(); } } @@ -1947,7 +1912,7 @@ { PatternPosition pos; Configuration::PasteMode mode = config_->getPasteMode(); - if ((mode == Configuration::SELECTION || mode == Configuration::FILL) + if ((mode == Configuration::PasteMode::Selection || mode == Configuration::PasteMode::Fill) && selLeftAbovePos_.colInTrack != -1) { pos = selLeftAbovePos_; pos.colInTrack = pasteCol; @@ -2223,28 +2188,28 @@ copy->setShortcut(QKeySequence(Qt::CTRL + Qt::Key_C)); cut->setShortcut(QKeySequence(Qt::CTRL + Qt::Key_X)); paste->setShortcut(QKeySequence(Qt::CTRL + Qt::Key_V)); - pasteMix->setShortcut(gui_utils::strToKeySeq(shortcuts.at(Configuration::PasteMix))); - pasteOver->setShortcut(gui_utils::strToKeySeq(shortcuts.at(Configuration::PasteOverwrite))); - pasteIns->setShortcut(gui_utils::strToKeySeq(shortcuts.at(Configuration::PasteInsert))); + pasteMix->setShortcut(gui_utils::strToKeySeq(shortcuts.at(Configuration::ShortcutAction::PasteMix))); + pasteOver->setShortcut(gui_utils::strToKeySeq(shortcuts.at(Configuration::ShortcutAction::PasteOverwrite))); + pasteIns->setShortcut(gui_utils::strToKeySeq(shortcuts.at(Configuration::ShortcutAction::PasteInsert))); erase->setShortcut(QKeySequence(Qt::Key_Delete)); - select->setShortcut(gui_utils::strToKeySeq(shortcuts.at(Configuration::SelectAll))); - interpolate->setShortcut(gui_utils::strToKeySeq(shortcuts.at(Configuration::Interpolate))); - reverse->setShortcut(gui_utils::strToKeySeq(shortcuts.at(Configuration::Reverse))); - replace->setShortcut(gui_utils::strToKeySeq(shortcuts.at(Configuration::ReplaceInstrument))); - expand->setShortcut(gui_utils::strToKeySeq(shortcuts.at(Configuration::ExpandPattern))); - shrink->setShortcut(gui_utils::strToKeySeq(shortcuts.at(Configuration::ShrinkPattern))); - deNote->setShortcut(gui_utils::strToKeySeq(shortcuts.at(Configuration::DecreaseNote))); - inNote->setShortcut(gui_utils::strToKeySeq(shortcuts.at(Configuration::IncreaseNote))); - deOct->setShortcut(gui_utils::strToKeySeq(shortcuts.at(Configuration::DecreaseOctave))); - inOct->setShortcut(gui_utils::strToKeySeq(shortcuts.at(Configuration::IncreaseOctave))); - fdeVal->setShortcut(gui_utils::strToKeySeq(shortcuts.at(Configuration::FineDecreaseValues))); - finVal->setShortcut(gui_utils::strToKeySeq(shortcuts.at(Configuration::FineIncreaseValues))); - cdeVal->setShortcut(gui_utils::strToKeySeq(shortcuts.at(Configuration::CoarseDecreaseValues))); - cinVal->setShortcut(gui_utils::strToKeySeq(shortcuts.at(Configuration::CoarseIncreaseValuse))); - toggle->setShortcut(gui_utils::strToKeySeq(shortcuts.at(Configuration::ToggleTrack))); - solo->setShortcut(gui_utils::strToKeySeq(shortcuts.at(Configuration::SoloTrack))); - exeff->setShortcut(gui_utils::strToKeySeq(shortcuts.at(Configuration::ExpandEffect))); - sheff->setShortcut(gui_utils::strToKeySeq(shortcuts.at(Configuration::ShrinkEffect))); + select->setShortcut(gui_utils::strToKeySeq(shortcuts.at(Configuration::ShortcutAction::SelectAll))); + interpolate->setShortcut(gui_utils::strToKeySeq(shortcuts.at(Configuration::ShortcutAction::Interpolate))); + reverse->setShortcut(gui_utils::strToKeySeq(shortcuts.at(Configuration::ShortcutAction::Reverse))); + replace->setShortcut(gui_utils::strToKeySeq(shortcuts.at(Configuration::ShortcutAction::ReplaceInstrument))); + expand->setShortcut(gui_utils::strToKeySeq(shortcuts.at(Configuration::ShortcutAction::ExpandPattern))); + shrink->setShortcut(gui_utils::strToKeySeq(shortcuts.at(Configuration::ShortcutAction::ShrinkPattern))); + deNote->setShortcut(gui_utils::strToKeySeq(shortcuts.at(Configuration::ShortcutAction::DecreaseNote))); + inNote->setShortcut(gui_utils::strToKeySeq(shortcuts.at(Configuration::ShortcutAction::IncreaseNote))); + deOct->setShortcut(gui_utils::strToKeySeq(shortcuts.at(Configuration::ShortcutAction::DecreaseOctave))); + inOct->setShortcut(gui_utils::strToKeySeq(shortcuts.at(Configuration::ShortcutAction::IncreaseOctave))); + fdeVal->setShortcut(gui_utils::strToKeySeq(shortcuts.at(Configuration::ShortcutAction::FineDecreaseValues))); + finVal->setShortcut(gui_utils::strToKeySeq(shortcuts.at(Configuration::ShortcutAction::FineIncreaseValues))); + cdeVal->setShortcut(gui_utils::strToKeySeq(shortcuts.at(Configuration::ShortcutAction::CoarseDecreaseValues))); + cinVal->setShortcut(gui_utils::strToKeySeq(shortcuts.at(Configuration::ShortcutAction::CoarseIncreaseValuse))); + toggle->setShortcut(gui_utils::strToKeySeq(shortcuts.at(Configuration::ShortcutAction::ToggleTrack))); + solo->setShortcut(gui_utils::strToKeySeq(shortcuts.at(Configuration::ShortcutAction::SoloTrack))); + exeff->setShortcut(gui_utils::strToKeySeq(shortcuts.at(Configuration::ShortcutAction::ExpandEffect))); + sheff->setShortcut(gui_utils::strToKeySeq(shortcuts.at(Configuration::ShortcutAction::ShrinkEffect))); if (bt_->isJamMode() || pos.order < 0 || pos.trackVisIdx < 0) { copy->setEnabled(false); @@ -2381,14 +2346,14 @@ { auto shortcuts = config_->getShortcuts(); - hlUpSc_.setKey(gui_utils::strToKeySeq(shortcuts.at(Configuration::PrevHighlighted))); - hlUpWSSc_.setKey(gui_utils::strToKeySeq("Shift+" + shortcuts.at(Configuration::PrevHighlighted))); - hlDnSc_.setKey(gui_utils::strToKeySeq(shortcuts.at(Configuration::NextHighlighted))); - hlDnWSSc_.setKey(gui_utils::strToKeySeq("Shift+" + shortcuts.at(Configuration::NextHighlighted))); - keyOffSc_.setKey(gui_utils::strToKeySeq(shortcuts.at(Configuration::KeyOff))); - echoBufSc_.setKey(gui_utils::strToKeySeq(shortcuts.at(Configuration::EchoBuffer))); - expandColSc_.setKey(gui_utils::strToKeySeq(shortcuts.at(Configuration::ExpandEffect))); - shrinkColSc_.setKey(gui_utils::strToKeySeq(shortcuts.at(Configuration::ShrinkEffect))); + hlUpSc_.setKey(gui_utils::strToKeySeq(shortcuts.at(Configuration::ShortcutAction::PrevHighlighted))); + hlUpWSSc_.setKey(gui_utils::strToKeySeq("Shift+" + shortcuts.at(Configuration::ShortcutAction::PrevHighlighted))); + hlDnSc_.setKey(gui_utils::strToKeySeq(shortcuts.at(Configuration::ShortcutAction::NextHighlighted))); + hlDnWSSc_.setKey(gui_utils::strToKeySeq("Shift+" + shortcuts.at(Configuration::ShortcutAction::NextHighlighted))); + keyOffSc_.setKey(gui_utils::strToKeySeq(shortcuts.at(Configuration::ShortcutAction::KeyOff))); + echoBufSc_.setKey(gui_utils::strToKeySeq(shortcuts.at(Configuration::ShortcutAction::EchoBuffer))); + expandColSc_.setKey(gui_utils::strToKeySeq(shortcuts.at(Configuration::ShortcutAction::ExpandEffect))); + shrinkColSc_.setKey(gui_utils::strToKeySeq(shortcuts.at(Configuration::ShortcutAction::ShrinkEffect))); } void PatternEditorPanel::setPatternHighlight1Count(int count) @@ -2815,24 +2780,40 @@ case 0: return enterToneData(event); case 1: - if (event->modifiers().testFlag(Qt::NoModifier)) return enterInstrumentData(event->key()); + { + auto modifiers = event->modifiers(); + if (modifiers.testFlag(Qt::NoModifier) || modifiers.testFlag(Qt::KeypadModifier)) + return enterInstrumentData(event->key()); break; + } case 2: - if (event->modifiers().testFlag(Qt::NoModifier)) return enterVolumeData(event->key()); + { + auto modifiers = event->modifiers(); + if (modifiers.testFlag(Qt::NoModifier) || modifiers.testFlag(Qt::KeypadModifier)) + return enterVolumeData(event->key()); break; + } case 3: case 5: case 7: case 9: - if (event->modifiers().testFlag(Qt::NoModifier)) return enterEffectID(event->key()); + { + auto modifiers = event->modifiers(); + if (modifiers.testFlag(Qt::NoModifier) || modifiers.testFlag(Qt::KeypadModifier)) + return enterEffectID(event->key()); break; + } case 4: case 6: case 8: case 10: - if (event->modifiers().testFlag(Qt::NoModifier)) return enterEffectValue(event->key()); + { + auto modifiers = event->modifiers(); + if (modifiers.testFlag(Qt::NoModifier) || modifiers.testFlag(Qt::KeypadModifier)) + return enterEffectValue(event->key()); break; } + } } return false; } @@ -2989,8 +2970,8 @@ solo->setShortcutVisibleInContextMenu(true); #endif auto shortcuts = config_->getShortcuts(); - toggle->setShortcut(gui_utils::strToKeySeq(shortcuts.at(Configuration::ToggleTrack))); - solo->setShortcut(gui_utils::strToKeySeq(shortcuts.at(Configuration::SoloTrack))); + toggle->setShortcut(gui_utils::strToKeySeq(shortcuts.at(Configuration::ShortcutAction::ToggleTrack))); + solo->setShortcut(gui_utils::strToKeySeq(shortcuts.at(Configuration::ShortcutAction::SoloTrack))); if (mousePressPos_.trackVisIdx < 0) { toggle->setEnabled(false); solo->setEnabled(false); @@ -3225,8 +3206,7 @@ if (!bt_->isJamMode()) { bool release = ((status & 0xf0) == 0x80) || velocity == 0; if (!release) { - std::pair octaveAndNote = noteNumberToOctaveAndNote(static_cast(key) - 12); - setStepKeyOn(octaveAndNote.second, octaveAndNote.first); + setStepKeyOn(Note(static_cast(key) - 12)); } } } diff -Nru bambootracker-0.4.5+git20121209/BambooTracker/gui/pattern_editor/pattern_editor_panel.hpp bambootracker-0.4.6/BambooTracker/gui/pattern_editor/pattern_editor_panel.hpp --- bambootracker-0.4.5+git20121209/BambooTracker/gui/pattern_editor/pattern_editor_panel.hpp 2020-12-08 14:05:28.000000000 +0000 +++ bambootracker-0.4.6/BambooTracker/gui/pattern_editor/pattern_editor_panel.hpp 2021-02-11 14:37:17.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (C) 2018-2020 Rerrah + * Copyright (C) 2018-2021 Rerrah * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation @@ -49,7 +49,6 @@ #include "song.hpp" #include "gui/pattern_editor/pattern_position.hpp" #include "gui/color_palette.hpp" -#include "misc.hpp" class PatternEditorPanel : public QWidget { @@ -276,7 +275,7 @@ } bool enterToneData(QKeyEvent* event); - void setStepKeyOn(Note note, int octave); + void setStepKeyOn(const Note& note); bool enterInstrumentData(int key); void setStepInstrument(int num); bool enterVolumeData(int key); diff -Nru bambootracker-0.4.5+git20121209/BambooTracker/gui/track_visibility_memory_handler.cpp bambootracker-0.4.6/BambooTracker/gui/track_visibility_memory_handler.cpp --- bambootracker-0.4.5+git20121209/BambooTracker/gui/track_visibility_memory_handler.cpp 2020-12-08 14:05:28.000000000 +0000 +++ bambootracker-0.4.6/BambooTracker/gui/track_visibility_memory_handler.cpp 2021-02-11 14:37:17.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (C) 2020 Rerrah + * Copyright (C) 2020-2021 Rerrah * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation @@ -28,18 +28,21 @@ #include #include #include +#include "song.hpp" +namespace io +{ +namespace +{ // config path (*nix): ~/.config//.ini -const QString TrackVisibilityMemoryHandler::ORGANIZATION_ = "BambooTracker"; -const QString TrackVisibilityMemoryHandler::FILE_ = "TrackVisibility"; - -TrackVisibilityMemoryHandler::TrackVisibilityMemoryHandler() {} +const QString ORG_NAME = "BambooTracker"; +const QString FILE_NAME = "TrackVisibility"; +} -bool TrackVisibilityMemoryHandler::saveTrackVisibilityMemory( - const SongType type, const std::vector& visTracks) +bool saveTrackVisibilityMemory(const SongType type, const std::vector& visTracks) { try { - QSettings settings(QSettings::IniFormat, QSettings::UserScope, ORGANIZATION_, FILE_); + QSettings settings(QSettings::IniFormat, QSettings::UserScope, ORG_NAME, FILE_NAME); int tid; switch (type) { @@ -55,16 +58,16 @@ settings.setValue("visTracks", str); return true; - } catch (...) { + } + catch (...) { return false; } } -bool TrackVisibilityMemoryHandler::loadTrackVisibilityMemory( - SongType& type, std::vector& visTracks) +bool loadTrackVisibilityMemory(SongType& type, std::vector& visTracks) { try { - QSettings settings(QSettings::IniFormat, QSettings::UserScope, ORGANIZATION_, FILE_); + QSettings settings(QSettings::IniFormat, QSettings::UserScope, ORG_NAME, FILE_NAME); if (!settings.contains("type")) return false; switch (settings.value("type", -1).toInt()) { @@ -89,7 +92,9 @@ visTracks.swap(tl); return true; - } catch (...) { + } + catch (...) { return false; } } +} diff -Nru bambootracker-0.4.5+git20121209/BambooTracker/gui/track_visibility_memory_handler.hpp bambootracker-0.4.6/BambooTracker/gui/track_visibility_memory_handler.hpp --- bambootracker-0.4.5+git20121209/BambooTracker/gui/track_visibility_memory_handler.hpp 2020-12-08 14:05:28.000000000 +0000 +++ bambootracker-0.4.6/BambooTracker/gui/track_visibility_memory_handler.hpp 2021-02-11 14:37:17.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (C) 2020 Rerrah + * Copyright (C) 2020-2021 Rerrah * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation @@ -27,20 +27,13 @@ #define TRACK_VISIBILITY_MEMORY_HANDLER_HPP #include -#include -#include "misc.hpp" -class TrackVisibilityMemoryHandler -{ -public: - static bool saveTrackVisibilityMemory(const SongType type, const std::vector& visTracks); - static bool loadTrackVisibilityMemory(SongType& type, std::vector& visTracks); - -private: - const static QString ORGANIZATION_; - const static QString FILE_; +enum class SongType; - TrackVisibilityMemoryHandler(); -}; +namespace io +{ +bool saveTrackVisibilityMemory(const SongType type, const std::vector& visTracks); +bool loadTrackVisibilityMemory(SongType& type, std::vector& visTracks); +} #endif // TRACK_VISIBILITY_MEMORY_HANDLER_HPP diff -Nru bambootracker-0.4.5+git20121209/BambooTracker/gui/vgm_export_settings_dialog.cpp bambootracker-0.4.6/BambooTracker/gui/vgm_export_settings_dialog.cpp --- bambootracker-0.4.5+git20121209/BambooTracker/gui/vgm_export_settings_dialog.cpp 2020-12-08 14:05:28.000000000 +0000 +++ bambootracker-0.4.6/BambooTracker/gui/vgm_export_settings_dialog.cpp 2021-02-11 14:37:17.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (C) 2018-2020 Rerrah + * Copyright (C) 2018-2021 Rerrah * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation @@ -118,74 +118,85 @@ endNull += '\0'; endNull += '\0'; + const QByteArray trackNameEn = getTrackNameEnglish().toLatin1(); tag.trackNameEn = ""; - for (const auto& c : getTrackNameEnglish().toLatin1()) { + for (const auto& c : trackNameEn) { tag.trackNameEn += c; tag.trackNameEn += '\0'; } tag.trackNameEn += endNull; + const QByteArray trackNameJp = sjis->fromUnicode(getTrackNameJapanese()); tag.trackNameJp = ""; - for (const auto& c : sjis->fromUnicode(getTrackNameJapanese())) { + for (const auto& c : trackNameJp) { tag.trackNameJp += c; } tag.trackNameJp += endNull; + const QByteArray gameNameEn = getGameNameEnglish().toLatin1(); tag.gameNameEn = ""; - for (const auto& c : getGameNameEnglish().toLatin1()) { + for (const auto& c : gameNameEn) { tag.gameNameEn += c; tag.gameNameEn += '\0'; } tag.gameNameEn += endNull; + const QByteArray gameNameJp = sjis->fromUnicode(getGameNameJapanese()); tag.gameNameJp = ""; - for (const auto& c : sjis->fromUnicode(getGameNameJapanese())) { + for (const auto& c : gameNameJp) { tag.gameNameJp += c; } tag.gameNameJp += endNull; + const QByteArray systemNameEn = getSystemNameEnglish().toLatin1(); tag.systemNameEn = ""; - for (const auto& c : getSystemNameEnglish().toLatin1()) { + for (const auto& c : systemNameEn) { tag.systemNameEn += c; tag.systemNameEn += '\0'; } tag.systemNameEn += endNull; + const QByteArray systemNameJp = sjis->fromUnicode(getSystemNameJapanese()); tag.systemNameJp = ""; - for (const auto& c : sjis->fromUnicode(getSystemNameJapanese())) { + for (const auto& c : systemNameJp) { tag.systemNameJp += c; } tag.systemNameJp += endNull; + const QByteArray authorEn = getTrackAuthorEnglish().toLatin1(); tag.authorEn = ""; - for (const auto& c : getTrackAuthorEnglish().toLatin1()) { + for (const auto& c : authorEn) { tag.authorEn += c; tag.authorEn += '\0'; } tag.authorEn += endNull; + const QByteArray authorJp = sjis->fromUnicode(getTrackAuthorJapanese()); tag.authorJp = ""; - for (const auto& c : sjis->fromUnicode(getTrackAuthorJapanese())) { + for (const auto& c : authorJp) { tag.authorJp += c; } tag.authorJp += endNull; + const QByteArray releaseDate = getReleaseDate().toLatin1(); tag.releaseDate = ""; - for (const auto& c : getReleaseDate().toLatin1()) { + for (const auto& c : releaseDate) { tag.releaseDate += c; tag.releaseDate += '\0'; } tag.releaseDate += endNull; + const QByteArray vgmCreator = getVgmCreator().toLatin1(); tag.vgmCreator = ""; - for (const auto& c : getVgmCreator().toLatin1()) { + for (const auto& c : vgmCreator) { tag.vgmCreator += c; tag.vgmCreator += '\0'; } tag.vgmCreator += endNull; + const QByteArray notes = getNotes().toLatin1(); tag.notes = ""; - for (const auto& c : getNotes().toLatin1()) { + for (const auto& c : notes) { tag.notes += c; tag.notes += '\0'; } diff -Nru bambootracker-0.4.5+git20121209/BambooTracker/gui/vgm_export_settings_dialog.hpp bambootracker-0.4.6/BambooTracker/gui/vgm_export_settings_dialog.hpp --- bambootracker-0.4.5+git20121209/BambooTracker/gui/vgm_export_settings_dialog.hpp 2020-12-08 14:05:28.000000000 +0000 +++ bambootracker-0.4.6/BambooTracker/gui/vgm_export_settings_dialog.hpp 2021-02-11 14:37:17.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (C) 2018-2019 Rerrah + * Copyright (C) 2018-2021 Rerrah * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation @@ -31,7 +31,7 @@ #include "io/export_io.hpp" namespace Ui { - class VgmExportSettingsDialog; +class VgmExportSettingsDialog; } class VgmExportSettingsDialog : public QDialog diff -Nru bambootracker-0.4.5+git20121209/BambooTracker/instrument/abstract_instrument_property.cpp bambootracker-0.4.6/BambooTracker/instrument/abstract_instrument_property.cpp --- bambootracker-0.4.5+git20121209/BambooTracker/instrument/abstract_instrument_property.cpp 2020-12-08 14:05:28.000000000 +0000 +++ bambootracker-0.4.6/BambooTracker/instrument/abstract_instrument_property.cpp 2021-02-11 14:37:17.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (C) 2018 Rerrah + * Copyright (C) 2018-2020 Rerrah * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation @@ -24,38 +24,21 @@ */ #include "abstract_instrument_property.hpp" -#include AbstractInstrumentProperty::AbstractInstrumentProperty(int num) : num_(num) { } -AbstractInstrumentProperty::AbstractInstrumentProperty(const AbstractInstrumentProperty& other) -{ - num_ = other.num_; - users_ = other.users_; -} - -void AbstractInstrumentProperty::setNumber(int num) -{ - num_ = num; -} - -int AbstractInstrumentProperty::getNumber() const -{ - return num_; -} - void AbstractInstrumentProperty::registerUserInstrument(int instNum) { - users_.push_back(instNum); - std::sort(users_.begin(), users_.end()); + users_.insert(instNum); } void AbstractInstrumentProperty::deregisterUserInstrument(int instNum) { - users_.erase(std::find(users_.begin(), users_.end(), instNum)); + auto&& it = users_.find(instNum); + if (it != users_.end()) users_.erase(it); } bool AbstractInstrumentProperty::isUserInstrument() const @@ -63,11 +46,6 @@ return !users_.empty(); } -std::vector AbstractInstrumentProperty::getUserInstruments() const -{ - return users_; -} - void AbstractInstrumentProperty::clearUserInstruments() { users_.clear(); diff -Nru bambootracker-0.4.5+git20121209/BambooTracker/instrument/abstract_instrument_property.hpp bambootracker-0.4.6/BambooTracker/instrument/abstract_instrument_property.hpp --- bambootracker-0.4.5+git20121209/BambooTracker/instrument/abstract_instrument_property.hpp 2020-12-08 14:05:28.000000000 +0000 +++ bambootracker-0.4.6/BambooTracker/instrument/abstract_instrument_property.hpp 2021-02-11 14:37:17.000000000 +0000 @@ -25,21 +25,20 @@ #pragma once -#include -#include +#include class AbstractInstrumentProperty { public: virtual ~AbstractInstrumentProperty() = default; - void setNumber(int num); - int getNumber() const; + inline void setNumber(int num) noexcept { num_ = num; } + inline int getNumber() const noexcept { return num_; } void registerUserInstrument(int instNum); void deregisterUserInstrument(int instNum); bool isUserInstrument() const; - std::vector getUserInstruments() const; + inline std::multiset getUserInstruments() const noexcept { return users_; } void clearUserInstruments(); virtual bool isEdited() const = 0; @@ -47,9 +46,8 @@ protected: explicit AbstractInstrumentProperty(int num); - AbstractInstrumentProperty(const AbstractInstrumentProperty& other); private: int num_; - std::vector users_; + std::multiset users_; }; diff -Nru bambootracker-0.4.5+git20121209/BambooTracker/instrument/bank.cpp bambootracker-0.4.6/BambooTracker/instrument/bank.cpp --- bambootracker-0.4.5+git20121209/BambooTracker/instrument/bank.cpp 2020-12-08 14:05:28.000000000 +0000 +++ bambootracker-0.4.6/BambooTracker/instrument/bank.cpp 2021-02-11 14:37:17.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (C) 2018-2020 Rerrah + * Copyright (C) 2018-2021 Rerrah * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation @@ -24,29 +24,31 @@ */ #include "bank.hpp" -#include -#include +#include #include "io/instrument_io.hpp" #include "io/opni_io.hpp" #include "io/btb_io.hpp" #include "io/ff_io.hpp" #include "io/ppc_io.hpp" +#include "io/p86_io.hpp" +#include "io/pps_io.hpp" #include "io/pvi_io.hpp" +#include "io/pzi_io.hpp" #include "io/dat_io.hpp" #include "format/wopn_file.h" -BtBank::BtBank(std::vector ids, std::vector names) - : ids_(std::move(ids)), - names_(std::move(names)) +BtBank::BtBank(const std::vector& ids, const std::vector& names) + : ids_(ids), + names_(names) { } -BtBank::BtBank(std::vector ids, std::vector names, - std::vector instSecs, io::BinaryContainer propSec, uint32_t version) - : instCtrs_(std::move(instSecs)), - propCtr_(std::move(propSec)), - ids_(std::move(ids)), - names_(std::move(names)), +BtBank::BtBank(const std::vector& ids, const std::vector& names, + const std::vector& instSecs, const io::BinaryContainer& propSec, uint32_t version) + : instCtrs_(instSecs), + propCtr_(propSec), + ids_(ids), + names_(names), version_(version) { } @@ -89,7 +91,7 @@ } vals; }; -WopnBank::WopnBank(WOPNFile *wopn) +WopnBank::WopnBank(WOPNFile* wopn) : wopn_(wopn) { unsigned numM = wopn->banks_count_melodic; @@ -122,27 +124,27 @@ std::string WopnBank::getInstrumentIdentifier(size_t index) const { - const InstEntry &ent = entries_.at(index); + const InstEntry& ent = entries_.at(index); char identifier[64]; - sprintf(identifier, "%c%03d:%03d:%03d", "MP"[ent.vals.percussive], + std::sprintf(identifier, "%c%03d:%03d:%03d", "MP"[ent.vals.percussive], ent.vals.msb, ent.vals.lsb, ent.vals.nth); return identifier; } std::string WopnBank::getInstrumentName(size_t index) const { - const InstEntry &ent = entries_.at(index); + const InstEntry& ent = entries_.at(index); return ent.inst->inst_name; } AbstractInstrument* WopnBank::loadInstrument(size_t index, std::weak_ptr instMan, int instNum) const { - const InstEntry &ent = entries_.at(index); + const InstEntry& ent = entries_.at(index); return io::OpniIO::loadWOPNInstrument(*ent.inst, instMan, instNum); } /******************************/ -FfBank::FfBank(std::vector ids, std::vector names, std::vector ctrs) +FfBank::FfBank(const std::vector& ids, const std::vector& names, const std::vector& ctrs) : ids_(ids), names_(names), instCtrs_(ctrs) { } @@ -173,7 +175,7 @@ } /******************************/ -PpcBank::PpcBank(std::vector ids, std::vector > samples) +PpcBank::PpcBank(const std::vector& ids, const std::vector>& samples) : ids_(ids), samples_(samples) { } @@ -200,11 +202,65 @@ } /******************************/ -PviBank::PviBank(std::vector ids, std::vector > samples) +P86Bank::P86Bank(const std::vector& ids, const std::vector>& samples) : ids_(ids), samples_(samples) { } +size_t P86Bank::getNumInstruments() const +{ + return samples_.size(); +} + +std::string P86Bank::getInstrumentIdentifier(size_t index) const +{ + return std::to_string(ids_.at(index)); +} + +std::string P86Bank::getInstrumentName(size_t index) const +{ + (void)index; + return ""; +} + +AbstractInstrument* P86Bank::loadInstrument(size_t index, std::weak_ptr instMan, int instNum) const +{ + return io::P86IO::loadInstrument(samples_.at(index), instMan, instNum); +} + +/******************************/ +PpsBank::PpsBank(const std::vector& ids, const std::vector>& samples) + : ids_(ids), samples_(samples) +{ +} + +size_t PpsBank::getNumInstruments() const +{ + return samples_.size(); +} + +std::string PpsBank::getInstrumentIdentifier(size_t index) const +{ + return std::to_string(ids_.at(index)); +} + +std::string PpsBank::getInstrumentName(size_t index) const +{ + (void)index; + return ""; +} + +AbstractInstrument* PpsBank::loadInstrument(size_t index, std::weak_ptr instMan, int instNum) const +{ + return io::PpsIO::loadInstrument(samples_.at(index), instMan, instNum); +} + +/******************************/ +PviBank::PviBank(const std::vector& ids, uint16_t deltaN, const std::vector>& samples) + : ids_(ids), deltaN_(deltaN), samples_(samples) +{ +} + size_t PviBank::getNumInstruments() const { return samples_.size(); @@ -223,11 +279,39 @@ AbstractInstrument* PviBank::loadInstrument(size_t index, std::weak_ptr instMan, int instNum) const { - return io::PviIO::loadInstrument(samples_.at(index), instMan, instNum); + return io::PviIO::loadInstrument(samples_.at(index), deltaN_, instMan, instNum); +} + +/******************************/ +PziBank::PziBank(const std::vector& ids, const std::vector& deltaNs, + const std::vector& isRepeatedList, const std::vector>& samples) + : ids_(ids), deltaNs_(deltaNs), isRepeatedList_(isRepeatedList), samples_(samples) +{ +} + +size_t PziBank::getNumInstruments() const +{ + return samples_.size(); +} + +std::string PziBank::getInstrumentIdentifier(size_t index) const +{ + return std::to_string(ids_.at(index)); +} + +std::string PziBank::getInstrumentName(size_t index) const +{ + (void)index; + return ""; +} + +AbstractInstrument* PziBank::loadInstrument(size_t index, std::weak_ptr instMan, int instNum) const +{ + return io::PziIO::loadInstrument(samples_.at(index), deltaNs_.at(index), isRepeatedList_.at(index), instMan, instNum); } /******************************/ -Mucom88Bank::Mucom88Bank(std::vector ids, std::vector names, std::vector ctrs) +Mucom88Bank::Mucom88Bank(const std::vector& ids, const std::vector& names, const std::vector& ctrs) : ids_(ids), names_(names), instCtrs_(ctrs) { } diff -Nru bambootracker-0.4.5+git20121209/BambooTracker/instrument/bank.hpp bambootracker-0.4.6/BambooTracker/instrument/bank.hpp --- bambootracker-0.4.5+git20121209/BambooTracker/instrument/bank.hpp 2020-12-08 14:05:28.000000000 +0000 +++ bambootracker-0.4.6/BambooTracker/instrument/bank.hpp 2021-02-11 14:37:17.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (C) 2018-2020 Rerrah + * Copyright (C) 2018-2021 Rerrah * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation @@ -27,6 +27,7 @@ #include #include +#include #include "io/binary_container.hpp" class AbstractInstrument; @@ -44,12 +45,12 @@ virtual AbstractInstrument* loadInstrument(size_t index, std::weak_ptr instMan, int instNum) const = 0; }; -class BtBank : public AbstractBank +class BtBank final : public AbstractBank { public: - BtBank(std::vector ids, std::vector names); - BtBank(std::vector ids, std::vector names, - std::vector instSecs, io::BinaryContainer propSec, uint32_t version); + BtBank(const std::vector& ids, const std::vector& names); + BtBank(const std::vector& ids, const std::vector& names, + const std::vector& instSecs, const io::BinaryContainer& propSec, uint32_t version); size_t getNumInstruments() const override; std::string getInstrumentIdentifier(size_t index) const override; @@ -64,7 +65,7 @@ uint32_t version_; }; -class WopnBank : public AbstractBank +class WopnBank final : public AbstractBank { public: explicit WopnBank(WOPNFile *wopn); @@ -84,10 +85,10 @@ std::vector entries_; }; -class FfBank : public AbstractBank +class FfBank final : public AbstractBank { public: - explicit FfBank(std::vector ids, std::vector names, std::vector ctrs); + FfBank(const std::vector& ids, const std::vector& names, const std::vector& ctrs); size_t getNumInstruments() const override; std::string getInstrumentIdentifier(size_t index) const override; @@ -102,10 +103,10 @@ std::vector instCtrs_; }; -class PpcBank : public AbstractBank +class PpcBank final : public AbstractBank { public: - explicit PpcBank(std::vector ids, std::vector> samples); + PpcBank(const std::vector& ids, const std::vector>& samples); size_t getNumInstruments() const override; std::string getInstrumentIdentifier(size_t index) const override; @@ -117,10 +118,10 @@ std::vector> samples_; }; -class PviBank : public AbstractBank +class P86Bank final : public AbstractBank { public: - explicit PviBank(std::vector ids, std::vector> samples); + P86Bank(const std::vector& ids, const std::vector>& samples); size_t getNumInstruments() const override; std::string getInstrumentIdentifier(size_t index) const override; @@ -132,10 +133,59 @@ std::vector> samples_; }; -class Mucom88Bank : public AbstractBank +class PpsBank final : public AbstractBank { public: - explicit Mucom88Bank(std::vector ids, std::vector names, std::vector ctrs); + PpsBank(const std::vector& ids, const std::vector>& samples); + + size_t getNumInstruments() const override; + std::string getInstrumentIdentifier(size_t index) const override; + std::string getInstrumentName(size_t index) const override; + AbstractInstrument* loadInstrument(size_t index, std::weak_ptr instMan, int instNum) const override; + +private: + std::vector ids_; + std::vector> samples_; +}; + +class PviBank final : public AbstractBank +{ +public: + PviBank(const std::vector& ids, uint16_t deltaN, const std::vector>& samples); + + size_t getNumInstruments() const override; + std::string getInstrumentIdentifier(size_t index) const override; + std::string getInstrumentName(size_t index) const override; + AbstractInstrument* loadInstrument(size_t index, std::weak_ptr instMan, int instNum) const override; + +private: + std::vector ids_; + uint16_t deltaN_; + std::vector> samples_; +}; + +class PziBank final : public AbstractBank +{ +public: + PziBank(const std::vector& ids, const std::vector& deltaNs, + const std::vector& isRepeatedList, const std::vector>& samples); + + size_t getNumInstruments() const override; + std::string getInstrumentIdentifier(size_t index) const override; + std::string getInstrumentName(size_t index) const override; + AbstractInstrument* loadInstrument(size_t index, std::weak_ptr instMan, int instNum) const override; + +private: + std::vector ids_; + std::vector deltaNs_; + std::vector isRepeatedList_; + std::vector> samples_; +}; + +class Mucom88Bank final : public AbstractBank +{ +public: + Mucom88Bank(const std::vector& ids, const std::vector& names, const std::vector& ctrs); size_t getNumInstruments() const override; std::string getInstrumentIdentifier(size_t index) const override; diff -Nru bambootracker-0.4.5+git20121209/BambooTracker/instrument/command_sequence.cpp bambootracker-0.4.6/BambooTracker/instrument/command_sequence.cpp --- bambootracker-0.4.5+git20121209/BambooTracker/instrument/command_sequence.cpp 2020-12-08 14:05:28.000000000 +0000 +++ bambootracker-0.4.6/BambooTracker/instrument/command_sequence.cpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,373 +0,0 @@ -/* - * Copyright (C) 2018-2020 Rerrah - * - * Permission is hereby granted, free of charge, to any person - * obtaining a copy of this software and associated documentation - * files (the "Software"), to deal in the Software without - * restriction, including without limitation the rights to use, - * copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following - * conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES - * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT - * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, - * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR - * OTHER DEALINGS IN THE SOFTWARE. - */ - -#include "command_sequence.hpp" - -CommandSequence::CommandSequence(int num, SequenceType seqType, int comType, int comData) - : AbstractInstrumentProperty(num), - DEF_COM_TYPE_(comType), - DEF_COM_DATA_(comData), - DEF_SEQ_TYPE_(seqType) -{ - clearParameters(); -} - -CommandSequence::CommandSequence(const CommandSequence& other) - : AbstractInstrumentProperty(other), - DEF_COM_TYPE_(other.DEF_COM_TYPE_), - DEF_COM_DATA_(other.DEF_COM_DATA_), - DEF_SEQ_TYPE_(other.DEF_SEQ_TYPE_), - type_(other.type_), - seq_(other.seq_), - loops_(other.loops_), - release_(other.release_) -{ -} - -bool operator==(const CommandSequence& a, const CommandSequence& b) -{ - return (a.type_ == b.type_ && a.seq_ == b.seq_ && a.loops_ == b.loops_ && a.release_ == b.release_); -} - -std::unique_ptr CommandSequence::clone() -{ - std::unique_ptr clone = std::make_unique(*this); - clone->clearUserInstruments(); - return clone; -} - -void CommandSequence::setType(SequenceType type) -{ - type_ = type; -} - -SequenceType CommandSequence::getType() const -{ - return type_; -} - -size_t CommandSequence::getSequenceSize() const -{ - return seq_.size(); -} - -int CommandSequence::getSequenceTypeAt(int n) -{ - return seq_.at(static_cast(n)).type; -} - -int CommandSequence::getSequenceDataAt(int n) -{ - return seq_.at(static_cast(n)).data; -} - -std::vector CommandSequence::getSequence() const -{ - return seq_; -} - -void CommandSequence::addSequenceCommand(int type, int data) -{ - seq_.push_back({ type, data }); -} - -void CommandSequence::removeSequenceCommand() -{ - seq_.pop_back(); - - // Modify loop - if (!loops_.empty()) { - if (static_cast(seq_.size()) == loops_.back().begin) { - loops_.pop_back(); - } - else if (static_cast(seq_.size()) == loops_.back().end) { - --loops_.back().end; - } - } - - // Modify release - if (release_.begin == static_cast(seq_.size())) - release_.begin = -1; -} - -void CommandSequence::setSequenceCommand(int n, int type, int data) -{ - seq_.at(static_cast(n)) = { type, data }; -} - -size_t CommandSequence::getNumberOfLoops() const -{ - return loops_.size(); -} - -int CommandSequence::getBeginningCountOfLoop(int n) -{ - return loops_.at(static_cast(n)).begin; -} - -int CommandSequence::getEndCountOfLoop(int n) -{ - return loops_.at(static_cast(n)).end; -} - -int CommandSequence::getTimesOfLoop(int n) -{ - return loops_.at(static_cast(n)).times; -} - -std::vector CommandSequence::getLoops() const -{ - return loops_; -} - -void CommandSequence::setLoops(std::vector begins, std::vector ends, std::vector times) -{ - loops_.clear(); - for (size_t i = 0; i < begins.size(); ++i) { - loops_.push_back({ begins.at(i), ends.at(i), times.at(i) }); - } -} - -int CommandSequence::getReleaseBeginningCount() const -{ - return release_.begin; -} - -ReleaseType CommandSequence::getReleaseType() const -{ - return release_.type; -} - -Release CommandSequence::getRelease() const -{ - return release_; -} - -void CommandSequence::setRelease(ReleaseType type, int begin) -{ - release_ = { type, begin }; -} - -std::unique_ptr CommandSequence::getIterator() -{ - return std::make_unique(this); -} - -bool CommandSequence::isEdited() const -{ - return (seq_.size() != 1 || seq_.front().type != DEF_COM_TYPE_ || seq_.front().data != DEF_COM_DATA_ - || loops_.size() || release_.begin > -1); -} - -void CommandSequence::clearParameters() -{ - type_ = DEF_SEQ_TYPE_; - seq_ = {{ DEF_COM_TYPE_, DEF_COM_DATA_ }}; - loops_.clear(); - release_ = { ReleaseType::NoRelease, -1 }; -} - -/****************************************/ -CommandSequence::Iterator::Iterator(CommandSequence* seq) - : seq_(seq), - pos_(0), - started_(false), - isRelease_(false), - relReleaseRatio_(1) -{ -} - -int CommandSequence::Iterator::getPosition() const -{ - return pos_; -} - -int CommandSequence::Iterator::getSequenceType() const -{ - return seq_->type_; -} - -int CommandSequence::Iterator::getCommandType() const -{ - return (pos_ == -1 || pos_ >= static_cast(seq_->getSequenceSize())) - ? -1 - : isRelease_ - ? static_cast(seq_->getSequenceTypeAt(pos_) * relReleaseRatio_) - : seq_->getSequenceTypeAt(pos_); -} - -int CommandSequence::Iterator::getCommandData() const -{ - return (pos_ == -1 - || pos_ >= static_cast(seq_->getSequenceSize())) ? -1 - : seq_->getSequenceDataAt(pos_); -} - -int CommandSequence::Iterator::next(bool isReleaseBegin) -{ - if (!isReleaseBegin && pos_ == -1) return -1; - - if (!started_) { - started_ = true; - return pos_; - } - - int next = -1; - if (isReleaseBegin) { - loopStack_.clear(); - isRelease_ = true; - switch (seq_->release_.type) { - case ReleaseType::NoRelease: - break; - case ReleaseType::FixedRelease: - next = seq_->release_.begin; - break; - case ReleaseType::AbsoluteRelease: - { - int crtr; - if (pos_ == -1) { - int prevIdx = seq_->release_.begin - 1; - if (prevIdx < 0) { - next = seq_->release_.begin; - break; - } - else { - crtr = seq_->seq_[static_cast(prevIdx)].type; - } - } - else { - crtr = seq_->seq_[static_cast(pos_)].type; - } - - for (size_t i = static_cast(seq_->release_.begin); i < seq_->seq_.size(); ++i) { - if (seq_->seq_[i].type <= crtr) { - next = static_cast(i); - break; - } - } - break; - } - case ReleaseType::RelativeRelease: - { - if (pos_ == -1) { - int prevIdx = seq_->release_.begin - 1; - if (prevIdx >= 0) relReleaseRatio_ = seq_->seq_[static_cast(prevIdx)].type / 15.0f; - } - else { - relReleaseRatio_ = seq_->seq_[static_cast(pos_)].type / 15.0f; - } - next = seq_->release_.begin; - break; - } - } - } - else { - next = pos_ + 1; - } - - while (!loopStack_.empty()) { - if (pos_ == loopStack_.back().end) { - if (loopStack_.back().times < 0) { // Infinity loop - next = loopStack_.back().begin; - break; - } - else { - if (loopStack_.back().times) { - next = loopStack_.back().begin; - --loopStack_.back().times; - break; - } - else { - loopStack_.pop_back(); - } - } - } - else { - break; - } - } - - for (auto& l : seq_->loops_) { - if (next < l.begin) break; - else if (next == l.begin) { - if (loopStack_.empty()) { - loopStack_.push_back({ l.begin, l.end, (l.times == 1) ? -1 : (l.times - 1)}); - } - else { - bool flag = true; - for (auto& lp : loopStack_) { - if (lp.begin == l.begin && lp.end == l.end) { - flag = false; - break; - } - } - if (flag) { - loopStack_.push_back({ l.begin, l.end, (l.times == 1) ? -1 : (l.times - 1)}); - } - } - } - } - pos_ = next; - - if (!isRelease_ && pos_ == seq_->release_.begin) { - pos_ = -1; - } - else if (pos_ == static_cast(seq_->seq_.size())) { - pos_ = -1; - } - - return pos_; -} - -int CommandSequence::Iterator::front() -{ - started_ = true; - loopStack_.clear(); - isRelease_ = false; - relReleaseRatio_ = 1; - - if (seq_->release_.begin == 0) { - pos_ = -1; - } - else { - pos_ = 0; - - for (auto& l : seq_->loops_) { - if (pos_ < l.begin) break; - else if (pos_ == l.begin) { - loopStack_.push_back({ l.begin, l.end, (l.times == 1) ? -1 : (l.times - 1)}); - } - } - } - - return pos_; -} - -int CommandSequence::Iterator::end() -{ - pos_ = -1; - started_ = false; - return -1; -} diff -Nru bambootracker-0.4.5+git20121209/BambooTracker/instrument/command_sequence.hpp bambootracker-0.4.6/BambooTracker/instrument/command_sequence.hpp --- bambootracker-0.4.5+git20121209/BambooTracker/instrument/command_sequence.hpp 2020-12-08 14:05:28.000000000 +0000 +++ bambootracker-0.4.6/BambooTracker/instrument/command_sequence.hpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,204 +0,0 @@ -/* - * Copyright (C) 2018-2020 Rerrah - * - * Permission is hereby granted, free of charge, to any person - * obtaining a copy of this software and associated documentation - * files (the "Software"), to deal in the Software without - * restriction, including without limitation the rights to use, - * copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following - * conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES - * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT - * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, - * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR - * OTHER DEALINGS IN THE SOFTWARE. - */ - -#pragma once -#include -#include -#include -#include "abstract_instrument_property.hpp" -#include "sequence_iterator_interface.hpp" - -struct CommandSequenceUnit -{ - int type; - /// In SSG waveform and envelope, - /// - If bit 17 is 0, - /// * If bit 16 is 0, bit 0-15 is raw data - /// * If bit 16 is 1, bit 0-7 is 2nd and bit 8-15 is 1st ratio value - /// - If bit 17 is 1, - /// * If bit 16 is 0, bit 0-15 is right shift value - /// * If bit 16 is 1, bit 0-15 is left shift value - int data; - - friend bool operator==(const CommandSequenceUnit& a, const CommandSequenceUnit& b) - { - return (a.type == b.type && a.data == b.data); - } - - friend bool operator!=(const CommandSequenceUnit& a, const CommandSequenceUnit& b) { return !(a == b); } - - enum DataType : int - { - NODATA = -1, - RAW, - RATIO, - LSHIFT, - RSHIFT - }; - - inline static DataType checkDataType(int data) - { - if (data < 0) return DataType::NODATA; - else if (0x20000 & data) return (0x10000 & data ? DataType::LSHIFT : DataType::RSHIFT); - else if (0x10000 & data) return DataType::RATIO; - else return DataType::RAW; - } - - inline static int ratio2data(int first, int second) - { - return ((1 << 16) | (first << 8) | second); - } - - inline static std::pair data2ratio(int data) - { - return std::make_pair((data & 0x0000ff00) >> 8, data & 0x000000ff); - } - - inline static int shift2data(int rshift) - { - if (rshift > 0) return ((2 << 16) | rshift); - else return ((3 << 16) | -rshift); - } - - /// Check whether data is left shift or right shift before call this method - inline static int data2shift(int data) - { - return 0xffff & data; - } -}; - -enum SequenceType : int -{ - NO_SEQUENCE_TYPE = -1, - ABSOLUTE_SEQUENCE = 0, - FIXED_SEQUENCE = 1, - RELATIVE_SEQUENCE = 2 -}; - -struct Loop -{ - int begin, end, times; - - friend bool operator==(const Loop& a, const Loop& b) - { - return (a.begin == b.begin && a.end == b.end && a.times == b.times); - } - - friend bool operator!=(const Loop& a, const Loop& b) { return !(a == b); } -}; - -enum ReleaseType -{ - NoRelease, - FixedRelease, - AbsoluteRelease, - RelativeRelease -}; - -struct Release -{ - ReleaseType type; - int begin; - - friend bool operator==(const Release& a, const Release& b) - { - return (a.type == b.type && a.begin == b.begin); - } - - friend bool operator!=(const Release& a, const Release& b) { return !(a == b); } -}; - -class CommandSequence : public AbstractInstrumentProperty -{ -public: - CommandSequence(int num, SequenceType seqType = SequenceType::NO_SEQUENCE_TYPE, int comType = 0, int comData = -1); - CommandSequence(const CommandSequence& other); - virtual ~CommandSequence() = default; - friend bool operator==(const CommandSequence& a, const CommandSequence& b); - friend bool operator!=(const CommandSequence& a, const CommandSequence& b) { return !(a == b); } - std::unique_ptr clone(); - - /// 0: Absolute - /// 1: Fix - /// 2: Relative - void setType(SequenceType type); - SequenceType getType() const; - - size_t getSequenceSize() const; - int getSequenceTypeAt(int n); - int getSequenceDataAt(int n); - std::vector getSequence() const; - void addSequenceCommand(int type, int data); - void removeSequenceCommand(); - void setSequenceCommand(int n, int type, int data); - - size_t getNumberOfLoops() const; - int getBeginningCountOfLoop(int n); - int getEndCountOfLoop(int n); - int getTimesOfLoop(int n); - std::vector getLoops() const; - void setLoops(std::vector begins, std::vector ends, std::vector times); - - int getReleaseBeginningCount() const; - ReleaseType getReleaseType() const; - Release getRelease() const; - void setRelease(ReleaseType type, int begin); - - class Iterator : public SequenceIteratorInterface - { - public: - explicit Iterator(CommandSequence* seq); - int getPosition() const override; - int getSequenceType() const override; - int getCommandType() const override; - int getCommandData() const override; - int next(bool isReleaseBegin = false) override; - int front() override; - int end() override; - - private: - CommandSequence* seq_; - int pos_; - bool started_; - std::vector loopStack_; - bool isRelease_; - float relReleaseRatio_; - }; - - std::unique_ptr getIterator(); - - bool isEdited() const override; - void clearParameters() override; - -private: - const int DEF_COM_TYPE_; - const int DEF_COM_DATA_; - const SequenceType DEF_SEQ_TYPE_; - - SequenceType type_; - std::vector seq_; - std::vector loops_; - Release release_; -}; diff -Nru bambootracker-0.4.5+git20121209/BambooTracker/instrument/effect_iterator.cpp bambootracker-0.4.6/BambooTracker/instrument/effect_iterator.cpp --- bambootracker-0.4.5+git20121209/BambooTracker/instrument/effect_iterator.cpp 2020-12-08 14:05:28.000000000 +0000 +++ bambootracker-0.4.6/BambooTracker/instrument/effect_iterator.cpp 2021-02-11 14:37:17.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (C) 2018-2020 Rerrah + * Copyright (C) 2018-2021 Rerrah * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation @@ -24,46 +24,34 @@ */ #include "effect_iterator.hpp" -#include "calc_pitch.hpp" +#include "note.hpp" #include -ArpeggioEffectIterator::ArpeggioEffectIterator(int second, int third) - : pos_(2), - started_(false), - second_(second + 48), - third_(third + 48) -{ -} - -int ArpeggioEffectIterator::getPosition() const +namespace { - return pos_; +const InstrumentSequenceBaseUnit ARP_CENTER(Note::DEFAULT_NOTE_NUM); } -int ArpeggioEffectIterator::getSequenceType() const +ArpeggioEffectIterator::ArpeggioEffectIterator(int second, int third) + : SequenceIteratorInterface(2), + started_(false), + second_(second + ARP_CENTER.data), + third_(third + ARP_CENTER.data) { - return 0; } -int ArpeggioEffectIterator::getCommandType() const +InstrumentSequenceBaseUnit ArpeggioEffectIterator::data() const noexcept { switch (pos_) { - case 0: return 48; + case 0: return ARP_CENTER; case 1: return second_; case 2: return third_; - default: return -1; + default: return InstrumentSequenceBaseUnit(); } } -int ArpeggioEffectIterator::getCommandData() const +int ArpeggioEffectIterator::next() { - return -1; -} - -int ArpeggioEffectIterator::next(bool isReleaseBegin) -{ - (void)isReleaseBegin; - if (started_) { pos_ = (pos_ + 1) % 3; } @@ -83,53 +71,35 @@ int ArpeggioEffectIterator::end() { - pos_ = -1; + pos_ = END_SEQ_POS; started_ = false; - return -1; + return END_SEQ_POS; } -/****************************************/ WavingEffectIterator::WavingEffectIterator(int period, int depth) : started_(false) { for (int i = 0; i <= period; ++i) { - seq_.push_back(i * depth); + seq_.emplace_back(i * depth); } for (size_t i = static_cast(period - 1); i > 0; --i) { seq_.push_back(seq_.at(i)); } size_t p2 = static_cast(period) << 1; for (size_t i = 0; i < p2; ++i) { - seq_.push_back(-seq_.at(i)); + seq_.emplace_back(-seq_.at(i).data); } pos_ = static_cast(seq_.size()) - 1; } -int WavingEffectIterator::getPosition() const -{ - return pos_; -} - -int WavingEffectIterator::getSequenceType() const -{ - return 0; -} - -int WavingEffectIterator::getCommandType() const +InstrumentSequenceBaseUnit WavingEffectIterator::data() const { - return seq_.at(static_cast(pos_)); + return (hasEnded() ? InstrumentSequenceBaseUnit() : seq_.at(static_cast(pos_))); } -int WavingEffectIterator::getCommandData() const +int WavingEffectIterator::next() { - return -1; -} - -int WavingEffectIterator::next(bool isReleaseBegin) -{ - (void)isReleaseBegin; - if (started_) { pos_ = (pos_ + 1) % static_cast(seq_.size()); } @@ -149,61 +119,43 @@ int WavingEffectIterator::end() { - pos_ = -1; + pos_ = END_SEQ_POS; started_ = false; - return -1; + return END_SEQ_POS; } -/****************************************/ NoteSlideEffectIterator::NoteSlideEffectIterator(int speed, int seminote) - : started_(false) + : SequenceIteratorInterface(0), + started_(false) { - int d = seminote * calc_pitch::SEMINOTE_PITCH; + int d = seminote * Note::SEMINOTE_PITCH; if (speed) { int prev = 0; for (int i = 0; i <= speed; ++i) { int dif = d * i / speed - prev; - seq_.push_back(dif); + seq_.emplace_back(dif); prev += dif; } } else { - seq_.push_back(d); + seq_.emplace_back(d); } - pos_ = 0; -} - -int NoteSlideEffectIterator::getPosition() const -{ - return pos_; } -int NoteSlideEffectIterator::getSequenceType() const +InstrumentSequenceBaseUnit NoteSlideEffectIterator::data() const { - return 0; -} - -int NoteSlideEffectIterator::getCommandType() const -{ - return seq_.at(static_cast(pos_)); -} - -int NoteSlideEffectIterator::getCommandData() const -{ - return -1; + return (hasEnded() ? InstrumentSequenceBaseUnit() : seq_.at(static_cast(pos_))); } -int NoteSlideEffectIterator::next(bool isReleaseBegin) +int NoteSlideEffectIterator::next() { - (void)isReleaseBegin; - - if (started_) { - return (++pos_ < static_cast(seq_.size())) ? pos_ : -1; + if (started_ && !hasEnded()) { + if (++pos_ >= static_cast(seq_.size())) pos_ = END_SEQ_POS; } else { started_ = true; - return pos_; } + return pos_; } int NoteSlideEffectIterator::front() @@ -215,7 +167,7 @@ int NoteSlideEffectIterator::end() { - pos_ = -1; + pos_ = END_SEQ_POS; started_ = false; - return -1; + return END_SEQ_POS; } diff -Nru bambootracker-0.4.5+git20121209/BambooTracker/instrument/effect_iterator.hpp bambootracker-0.4.6/BambooTracker/instrument/effect_iterator.hpp --- bambootracker-0.4.5+git20121209/BambooTracker/instrument/effect_iterator.hpp 2020-12-08 14:05:28.000000000 +0000 +++ bambootracker-0.4.6/BambooTracker/instrument/effect_iterator.hpp 2021-02-11 14:37:17.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (C) 2018-2019 Rerrah + * Copyright (C) 2018-2021 Rerrah * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation @@ -26,57 +26,58 @@ #pragma once #include #include "sequence_iterator_interface.hpp" +#include "sequence_property.hpp" -class ArpeggioEffectIterator : public SequenceIteratorInterface +class ArpeggioEffectIterator : public SequenceIteratorInterface { public: ArpeggioEffectIterator(int second, int third); - int getPosition() const override; - int getSequenceType() const override; - int getCommandType() const override; - int getCommandData() const override; - int next(bool isReleaseBegin = false) override; + SequenceType type() const noexcept override { return SequenceType::AbsoluteSequence; } + + InstrumentSequenceBaseUnit data() const noexcept override; + + int next() override; int front() override; + inline int release() override { return next(); } int end() override; private: - int pos_; bool started_; - int second_, third_; + InstrumentSequenceBaseUnit second_, third_; }; -class WavingEffectIterator : public SequenceIteratorInterface +class WavingEffectIterator : public SequenceIteratorInterface { public: WavingEffectIterator(int period, int depth); - int getPosition() const override; - int getSequenceType() const override; - int getCommandType() const override; - int getCommandData() const override; - int next(bool isReleaseBegin = false) override; + SequenceType type() const noexcept override { return SequenceType::AbsoluteSequence; } + + InstrumentSequenceBaseUnit data() const override; + + int next() override; int front() override; + inline int release() override { return next(); } int end() override; private: - int pos_; bool started_; - std::vector seq_; + std::vector seq_; }; -class NoteSlideEffectIterator : public SequenceIteratorInterface +class NoteSlideEffectIterator : public SequenceIteratorInterface { public: NoteSlideEffectIterator(int speed, int seminote); - int getPosition() const override; - int getSequenceType() const override; - int getCommandType() const override; - int getCommandData() const override; - int next(bool isReleaseBegin = false) override; + SequenceType type() const noexcept override { return SequenceType::AbsoluteSequence; } + + InstrumentSequenceBaseUnit data() const override; + + int next() override; int front() override; + inline int release() override { return next(); } int end() override; private: - int pos_; bool started_; - std::vector seq_; + std::vector seq_; }; diff -Nru bambootracker-0.4.5+git20121209/BambooTracker/instrument/envelope_fm.cpp bambootracker-0.4.6/BambooTracker/instrument/envelope_fm.cpp --- bambootracker-0.4.5+git20121209/BambooTracker/instrument/envelope_fm.cpp 2020-12-08 14:05:28.000000000 +0000 +++ bambootracker-0.4.6/BambooTracker/instrument/envelope_fm.cpp 2021-02-11 14:37:17.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (C) 2018-2020 Rerrah + * Copyright (C) 2018-2021 Rerrah * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation @@ -25,81 +25,58 @@ #include "envelope_fm.hpp" -constexpr EnvelopeFM::FMOperator EnvelopeFM::DEF_OP[4]; +namespace +{ +const std::unordered_map DEF_PARAMS = { + { FMEnvelopeParameter::AL, 4 }, + { FMEnvelopeParameter::FB, 0 }, + { FMEnvelopeParameter::AR1, 31 }, + { FMEnvelopeParameter::DR1, 0 }, + { FMEnvelopeParameter::SR1, 0 }, + { FMEnvelopeParameter::RR1, 7 }, + { FMEnvelopeParameter::SL1, 0 }, + { FMEnvelopeParameter::TL1, 32 }, + { FMEnvelopeParameter::KS1, 0 }, + { FMEnvelopeParameter::ML1, 0 }, + { FMEnvelopeParameter::DT1, 0 }, + { FMEnvelopeParameter::SSGEG1, -1 }, + { FMEnvelopeParameter::AR2, 31 }, + { FMEnvelopeParameter::DR2, 0 }, + { FMEnvelopeParameter::SR2, 0 }, + { FMEnvelopeParameter::RR2, 7 }, + { FMEnvelopeParameter::SL2, 0 }, + { FMEnvelopeParameter::TL2, 0 }, + { FMEnvelopeParameter::KS2, 0 }, + { FMEnvelopeParameter::ML2, 0 }, + { FMEnvelopeParameter::DT2, 0 }, + { FMEnvelopeParameter::SSGEG2, -1 }, + { FMEnvelopeParameter::AR3, 31 }, + { FMEnvelopeParameter::DR3, 0 }, + { FMEnvelopeParameter::SR3, 0 }, + { FMEnvelopeParameter::RR3, 7 }, + { FMEnvelopeParameter::SL3, 0 }, + { FMEnvelopeParameter::TL3, 32 }, + { FMEnvelopeParameter::KS3, 0 }, + { FMEnvelopeParameter::ML3, 0 }, + { FMEnvelopeParameter::DT3, 0 }, + { FMEnvelopeParameter::SSGEG3, -1 }, + { FMEnvelopeParameter::AR4, 31 }, + { FMEnvelopeParameter::DR4, 0 }, + { FMEnvelopeParameter::SR4, 0 }, + { FMEnvelopeParameter::RR4, 7 }, + { FMEnvelopeParameter::SL4, 0 }, + { FMEnvelopeParameter::TL4, 0 }, + { FMEnvelopeParameter::KS4, 0 }, + { FMEnvelopeParameter::ML4, 0 }, + { FMEnvelopeParameter::DT4, 0 }, + { FMEnvelopeParameter::SSGEG4, -1 } +}; +} EnvelopeFM::EnvelopeFM(int num) : AbstractInstrumentProperty(num) { clearParameters(); - initParamMap(); -} - -void EnvelopeFM::initParamMap() -{ - paramMap_ = { - { FMEnvelopeParameter::AL, al_ }, - { FMEnvelopeParameter::FB, fb_ }, - { FMEnvelopeParameter::AR1, op_[0].ar_ }, - { FMEnvelopeParameter::DR1, op_[0].dr_ }, - { FMEnvelopeParameter::SR1, op_[0].sr_ }, - { FMEnvelopeParameter::RR1, op_[0].rr_ }, - { FMEnvelopeParameter::SL1, op_[0].sl_ }, - { FMEnvelopeParameter::TL1, op_[0].tl_ }, - { FMEnvelopeParameter::KS1, op_[0].ks_ }, - { FMEnvelopeParameter::ML1, op_[0].ml_ }, - { FMEnvelopeParameter::DT1, op_[0].dt_ }, - { FMEnvelopeParameter::SSGEG1, op_[0].ssgeg_ }, - { FMEnvelopeParameter::AR2, op_[1].ar_ }, - { FMEnvelopeParameter::DR2, op_[1].dr_ }, - { FMEnvelopeParameter::SR2, op_[1].sr_ }, - { FMEnvelopeParameter::RR2, op_[1].rr_ }, - { FMEnvelopeParameter::SL2, op_[1].sl_ }, - { FMEnvelopeParameter::TL2, op_[1].tl_ }, - { FMEnvelopeParameter::KS2, op_[1].ks_ }, - { FMEnvelopeParameter::ML2, op_[1].ml_ }, - { FMEnvelopeParameter::DT2, op_[1].dt_ }, - { FMEnvelopeParameter::SSGEG2, op_[1].ssgeg_ }, - { FMEnvelopeParameter::AR3, op_[2].ar_ }, - { FMEnvelopeParameter::DR3, op_[2].dr_ }, - { FMEnvelopeParameter::SR3, op_[2].sr_ }, - { FMEnvelopeParameter::RR3, op_[2].rr_ }, - { FMEnvelopeParameter::SL3, op_[2].sl_ }, - { FMEnvelopeParameter::TL3, op_[2].tl_ }, - { FMEnvelopeParameter::KS3, op_[2].ks_ }, - { FMEnvelopeParameter::ML3, op_[2].ml_ }, - { FMEnvelopeParameter::DT3, op_[2].dt_ }, - { FMEnvelopeParameter::SSGEG3, op_[2].ssgeg_ }, - { FMEnvelopeParameter::AR4, op_[3].ar_ }, - { FMEnvelopeParameter::DR4, op_[3].dr_ }, - { FMEnvelopeParameter::SR4, op_[3].sr_ }, - { FMEnvelopeParameter::RR4, op_[3].rr_ }, - { FMEnvelopeParameter::SL4, op_[3].sl_ }, - { FMEnvelopeParameter::TL4, op_[3].tl_ }, - { FMEnvelopeParameter::KS4, op_[3].ks_ }, - { FMEnvelopeParameter::ML4, op_[3].ml_ }, - { FMEnvelopeParameter::DT4, op_[3].dt_ }, - { FMEnvelopeParameter::SSGEG4, op_[3].ssgeg_ } - }; -} - -EnvelopeFM::EnvelopeFM(const EnvelopeFM& other) - : AbstractInstrumentProperty(other) -{ - al_ = other.al_; - fb_ = other.fb_; - - for (int i = 0; i < 4; ++i) - op_[i] = other.op_[i]; - - initParamMap(); -} - -bool operator==(const EnvelopeFM& a, const EnvelopeFM& b) { - if (a.al_ != b.al_ || a.fb_ != b.fb_) return false; - for (int i = 0; i< 4; ++i) { - if (a.op_[0] != b.op_[0]) return false; - } - return true; } std::unique_ptr EnvelopeFM::clone() @@ -111,48 +88,31 @@ bool EnvelopeFM::getOperatorEnabled(int num) const { - return op_[num].enabled_; + return isEnabledOp_.test(num); } void EnvelopeFM::setOperatorEnabled(int num, bool enabled) { - op_[num].enabled_ = enabled; + isEnabledOp_.set(num, enabled); } int EnvelopeFM::getParameterValue(FMEnvelopeParameter param) const { - return paramMap_.at(param); + return params_.at(param); } void EnvelopeFM::setParameterValue(FMEnvelopeParameter param, int value) { - paramMap_.at(param) = value; + params_.at(param) = value; } bool EnvelopeFM::isEdited() const { - if (al_ != DEF_AL || fb_ != DEF_FB) return true; - for (int i = 0; i < 4; ++i) { - if (op_[i].enabled_ != DEF_OP[i].enabled_ - || op_[i].ar_ != DEF_OP[i].ar_ - || op_[i].dr_ != DEF_OP[i].dr_ - || op_[i].sr_ != DEF_OP[i].sr_ - || op_[i].rr_ != DEF_OP[i].rr_ - || op_[i].sl_ != DEF_OP[i].sl_ - || op_[i].tl_ != DEF_OP[i].tl_ - || op_[i].ks_ != DEF_OP[i].ks_ - || op_[i].ml_ != DEF_OP[i].ml_ - || op_[i].dt_ != DEF_OP[i].dt_) - return true; - } - return false; + return (params_ != DEF_PARAMS || !isEnabledOp_.all()); } void EnvelopeFM::clearParameters() { - al_ = DEF_AL; - fb_ = DEF_FB; - for (int i = 0; i < 4; ++i) { - op_[i] = DEF_OP[i]; - } + params_ = DEF_PARAMS; + isEnabledOp_.set(); } diff -Nru bambootracker-0.4.5+git20121209/BambooTracker/instrument/envelope_fm.hpp bambootracker-0.4.6/BambooTracker/instrument/envelope_fm.hpp --- bambootracker-0.4.5+git20121209/BambooTracker/instrument/envelope_fm.hpp 2020-12-08 14:05:28.000000000 +0000 +++ bambootracker-0.4.6/BambooTracker/instrument/envelope_fm.hpp 2021-02-11 14:37:17.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (C) 2018-2020 Rerrah + * Copyright (C) 2018-2021 Rerrah * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation @@ -26,19 +26,30 @@ #pragma once #include +#include #include #include "abstract_instrument_property.hpp" #include "enum_hash.hpp" -enum class FMEnvelopeParameter; +enum class FMEnvelopeParameter +{ + AL, FB, + AR1, DR1, SR1, RR1, SL1, TL1, KS1, ML1, DT1, + AR2, DR2, SR2, RR2, SL2, TL2, KS2, ML2, DT2, + AR3, DR3, SR3, RR3, SL3, TL3, KS3, ML3, DT3, + AR4, DR4, SR4, RR4, SL4, TL4, KS4, ML4, DT4, + SSGEG1, SSGEG2, SSGEG3, SSGEG4 +}; -class EnvelopeFM : public AbstractInstrumentProperty +class EnvelopeFM final : public AbstractInstrumentProperty { public: explicit EnvelopeFM(int num); - EnvelopeFM(const EnvelopeFM& other); - friend bool operator==(const EnvelopeFM& a, const EnvelopeFM& b); + friend bool operator==(const EnvelopeFM& a, const EnvelopeFM& b) + { + return (a.params_ == b.params_ && a.isEnabledOp_ == b.isEnabledOp_); + } friend bool operator!=(const EnvelopeFM& a, const EnvelopeFM& b) { return !(a == b); } std::unique_ptr clone(); @@ -53,51 +64,6 @@ void clearParameters() override; private: - int al_; - int fb_; - struct FMOperator - { - bool enabled_; - int ar_; - int dr_; - int sr_; - int rr_; - int sl_; - int tl_; - int ks_; - int ml_; - int dt_; - int ssgeg_; // -1: No use - - friend bool operator==(const FMOperator& a, const FMOperator& b) { - return (a.enabled_ == b.enabled_ && a.ar_ == b.ar_ && a.dr_ == b.dr_ - && a.sr_ == b.sr_ && a.rr_ == b.rr_ && a.sl_ == b.sl_ && a.tl_ == b.tl_ - && a.ks_ == b.ks_ && a.ml_ == b.ml_ && a.dt_ == b.dt_ && a.ssgeg_ == b.ssgeg_); - } - friend bool operator!=(const FMOperator& a, const FMOperator& b) { return !(a == b); } - }; - FMOperator op_[4]; - - static constexpr int DEF_AL = 4; - static constexpr int DEF_FB = 0; - static constexpr FMOperator DEF_OP[4] = { - { true, 31, 0, 0, 7, 0, 32, 0, 0, 0, -1 }, - { true, 31, 0, 0, 7, 0, 0, 0, 0, 0, -1 }, - { true, 31, 0, 0, 7, 0, 32, 0, 0, 0, -1 }, - { true, 31, 0, 0, 7, 0, 0, 0, 0, 0, -1 } - }; - - std::unordered_map paramMap_; - - void initParamMap(); -}; - -enum class FMEnvelopeParameter -{ - AL, FB, - AR1, DR1, SR1, RR1, SL1, TL1, KS1, ML1, DT1, - AR2, DR2, SR2, RR2, SL2, TL2, KS2, ML2, DT2, - AR3, DR3, SR3, RR3, SL3, TL3, KS3, ML3, DT3, - AR4, DR4, SR4, RR4, SL4, TL4, KS4, ML4, DT4, - SSGEG1, SSGEG2, SSGEG3, SSGEG4 + std::unordered_map params_; + std::bitset<4> isEnabledOp_; }; diff -Nru bambootracker-0.4.5+git20121209/BambooTracker/instrument/instrument.cpp bambootracker-0.4.6/BambooTracker/instrument/instrument.cpp --- bambootracker-0.4.5+git20121209/BambooTracker/instrument/instrument.cpp 2020-12-08 14:05:28.000000000 +0000 +++ bambootracker-0.4.6/BambooTracker/instrument/instrument.cpp 2021-02-11 14:37:17.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (C) 2018-2020 Rerrah + * Copyright (C) 2018-2021 Rerrah * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation @@ -24,32 +24,16 @@ */ #include "instrument.hpp" -#include +#include "instruments_manager.hpp" +#include "utils.hpp" -AbstractInstrument::AbstractInstrument(int number, std::string name, InstrumentsManager* owner) +AbstractInstrument::AbstractInstrument(int number, SoundSource src, InstrumentType type, const std::string& name, InstrumentsManager* owner) : owner_(owner), number_(number), - name_(name) -{} - -int AbstractInstrument::getNumber() const -{ - return number_; -} - -void AbstractInstrument::setNumber(int n) + name_(name), + sndSrc_(src), + instType_(type) { - number_ = n; -} - -std::string AbstractInstrument::getName() const -{ - return name_; -} - -void AbstractInstrument::setName(std::string name) -{ - name_ = name; } bool AbstractInstrument::isRegisteredWithManager() const @@ -59,8 +43,8 @@ /****************************************/ -InstrumentFM::InstrumentFM(int number, std::string name, InstrumentsManager* owner) : - AbstractInstrument(number, name, owner), +InstrumentFM::InstrumentFM(int number, const std::string& name, InstrumentsManager* owner) : + AbstractInstrument(number, SoundSource::FM, InstrumentType::FM, name, owner), envNum_(0), lfoEnabled_(false), lfoNum_(0) @@ -182,44 +166,9 @@ }; } -SoundSource InstrumentFM::getSoundSource() const -{ - return SoundSource::FM; -} - -InstrumentType InstrumentFM::getType() const -{ - return InstrumentType::FM; -} - AbstractInstrument* InstrumentFM::clone() { - auto c = new InstrumentFM(number_, name_, owner_); - c->setEnvelopeNumber(envNum_); - c->setLFOEnabled(lfoEnabled_); - c->setLFONumber(lfoNum_); - for (auto pair : opSeqEnabled_) { - c->setOperatorSequenceEnabled(pair.first, pair.second); - c->setOperatorSequenceNumber(pair.first, opSeqNum_.at(pair.first)); - } - for (auto pair : arpEnabled_) { - c->setArpeggioEnabled(pair.first, pair.second); - c->setArpeggioNumber(pair.first, arpNum_.at(pair.first)); - c->setPitchEnabled(pair.first, ptEnabled_.at(pair.first)); - c->setPitchNumber(pair.first, ptNum_.at(pair.first)); - c->setEnvelopeResetEnabled(pair.first, envResetEnabled_.at(pair.first)); - } - return c; -} - -void InstrumentFM::setEnvelopeNumber(int n) -{ - envNum_ = n; -} - -int InstrumentFM::getEnvelopeNumber() const -{ - return envNum_; + return new InstrumentFM(*this); } int InstrumentFM::getEnvelopeParameter(FMEnvelopeParameter param) const @@ -232,41 +181,11 @@ return owner_->getEnvelopeFMOperatorEnabled(envNum_, n); } -void InstrumentFM::setLFOEnabled(bool enabled) -{ - lfoEnabled_ = enabled; -} - -bool InstrumentFM::getLFOEnabled() const -{ - return lfoEnabled_; -} - -void InstrumentFM::setLFONumber(int n) -{ - lfoNum_ = n; -} - -int InstrumentFM::getLFONumber() const -{ - return lfoNum_; -} - int InstrumentFM::getLFOParameter(FMLFOParameter param) const { return owner_->getLFOFMparameter(lfoNum_, param); } -void InstrumentFM::setEnvelopeResetEnabled(FMOperatorType op, bool enabled) -{ - envResetEnabled_.at(op) = enabled; -} - -bool InstrumentFM::getEnvelopeResetEnabled(FMOperatorType op) const -{ - return envResetEnabled_.at(op); -} - void InstrumentFM::setOperatorSequenceEnabled(FMEnvelopeParameter param, bool enabled) { opSeqEnabled_.at(param) = enabled; @@ -287,22 +206,22 @@ return opSeqNum_.at(param); } -std::vector InstrumentFM::getOperatorSequenceSequence(FMEnvelopeParameter param) const +std::vector InstrumentFM::getOperatorSequenceSequence(FMEnvelopeParameter param) const { return owner_->getOperatorSequenceFMSequence(param, opSeqNum_.at(param)); } -std::vector InstrumentFM::getOperatorSequenceLoops(FMEnvelopeParameter param) const +InstrumentSequenceLoopRoot InstrumentFM::getOperatorSequenceLoopRoot(FMEnvelopeParameter param) const { - return owner_->getOperatorSequenceFMLoops(param, opSeqNum_.at(param)); + return owner_->getOperatorSequenceFMLoopRoot(param, opSeqNum_.at(param)); } -Release InstrumentFM::getOperatorSequenceRelease(FMEnvelopeParameter param) const +InstrumentSequenceRelease InstrumentFM::getOperatorSequenceRelease(FMEnvelopeParameter param) const { return owner_->getOperatorSequenceFMRelease(param, opSeqNum_.at(param)); } -std::unique_ptr InstrumentFM::getOperatorSequenceSequenceIterator(FMEnvelopeParameter param) const +FMOperatorSequenceIter InstrumentFM::getOperatorSequenceSequenceIterator(FMEnvelopeParameter param) const { return owner_->getOperatorSequenceFMIterator(param, opSeqNum_.at(param)); } @@ -332,22 +251,22 @@ return owner_->getArpeggioFMType(arpNum_.at(op)); } -std::vector InstrumentFM::getArpeggioSequence(FMOperatorType op) const +std::vector InstrumentFM::getArpeggioSequence(FMOperatorType op) const { return owner_->getArpeggioFMSequence(arpNum_.at(op)); } -std::vector InstrumentFM::getArpeggioLoops(FMOperatorType op) const +InstrumentSequenceLoopRoot InstrumentFM::getArpeggioLoopRoot(FMOperatorType op) const { - return owner_->getArpeggioFMLoops(arpNum_.at(op)); + return owner_->getArpeggioFMLoopRoot(arpNum_.at(op)); } -Release InstrumentFM::getArpeggioRelease(FMOperatorType op) const +InstrumentSequenceRelease InstrumentFM::getArpeggioRelease(FMOperatorType op) const { return owner_->getArpeggioFMRelease(arpNum_.at(op)); } -std::unique_ptr InstrumentFM::getArpeggioSequenceIterator(FMOperatorType op) const +ArpeggioIter InstrumentFM::getArpeggioSequenceIterator(FMOperatorType op) const { return owner_->getArpeggioFMIterator(arpNum_.at(op)); } @@ -377,30 +296,40 @@ return owner_->getPitchFMType(ptNum_.at(op)); } -std::vector InstrumentFM::getPitchSequence(FMOperatorType op) const +std::vector InstrumentFM::getPitchSequence(FMOperatorType op) const { return owner_->getPitchFMSequence(ptNum_.at(op)); } -std::vector InstrumentFM::getPitchLoops(FMOperatorType op) const +InstrumentSequenceLoopRoot InstrumentFM::getPitchLoopRoot(FMOperatorType op) const { - return owner_->getPitchFMLoops(ptNum_.at(op)); + return owner_->getPitchFMLoopRoot(ptNum_.at(op)); } -Release InstrumentFM::getPitchRelease(FMOperatorType op) const +InstrumentSequenceRelease InstrumentFM::getPitchRelease(FMOperatorType op) const { return owner_->getPitchFMRelease(ptNum_.at(op)); } -std::unique_ptr InstrumentFM::getPitchSequenceIterator(FMOperatorType op) const +PitchIter InstrumentFM::getPitchSequenceIterator(FMOperatorType op) const { return owner_->getPitchFMIterator(ptNum_.at(op)); } +void InstrumentFM::setEnvelopeResetEnabled(FMOperatorType op, bool enabled) +{ + envResetEnabled_.at(op) = enabled; +} + +bool InstrumentFM::getEnvelopeResetEnabled(FMOperatorType op) const +{ + return envResetEnabled_.at(op); +} + /****************************************/ -InstrumentSSG::InstrumentSSG(int number, std::string name, InstrumentsManager* owner) - : AbstractInstrument(number, name, owner), +InstrumentSSG::InstrumentSSG(int number, const std::string& name, InstrumentsManager* owner) + : AbstractInstrument(number, SoundSource::SSG, InstrumentType::SSG, name, owner), wfEnabled_(false), wfNum_(0), tnEnabled_(false), @@ -414,246 +343,125 @@ { } -SoundSource InstrumentSSG::getSoundSource() const -{ - return SoundSource::SSG; -} - -InstrumentType InstrumentSSG::getType() const -{ - return InstrumentType::SSG; -} - AbstractInstrument* InstrumentSSG::clone() { - auto c = new InstrumentSSG(number_, name_, owner_); - c->setWaveformEnabled(wfEnabled_); - c->setWaveformNumber(wfNum_); - c->setToneNoiseEnabled(tnEnabled_); - c->setToneNoiseNumber(tnNum_); - c->setEnvelopeEnabled(envEnabled_); - c->setEnvelopeNumber(envNum_); - c->setArpeggioEnabled(arpEnabled_); - c->setArpeggioNumber(arpNum_); - c->setPitchEnabled(ptEnabled_); - c->setPitchNumber(ptNum_); - return c; -} - -void InstrumentSSG::setWaveformEnabled(bool enabled) -{ - wfEnabled_ = enabled; -} - -bool InstrumentSSG::getWaveformEnabled() const -{ - return wfEnabled_; + return new InstrumentSSG(*this); } -void InstrumentSSG::setWaveformNumber(int n) -{ - wfNum_ = n; -} - -int InstrumentSSG::getWaveformNumber() const -{ - return wfNum_; -} - -std::vector InstrumentSSG::getWaveformSequence() const +std::vector InstrumentSSG::getWaveformSequence() const { return owner_->getWaveformSSGSequence(wfNum_); } -std::vector InstrumentSSG::getWaveformLoops() const +InstrumentSequenceLoopRoot InstrumentSSG::getWaveformLoopRoot() const { - return owner_->getWaveformSSGLoops(wfNum_); + return owner_->getWaveformSSGLoopRoot(wfNum_); } -Release InstrumentSSG::getWaveformRelease() const +InstrumentSequenceRelease InstrumentSSG::getWaveformRelease() const { return owner_->getWaveformSSGRelease(wfNum_); } -std::unique_ptr InstrumentSSG::getWaveformSequenceIterator() const +SSGWaveformIter InstrumentSSG::getWaveformSequenceIterator() const { return owner_->getWaveformSSGIterator(wfNum_); } -void InstrumentSSG::setToneNoiseEnabled(bool enabled) -{ - tnEnabled_ = enabled; -} - -bool InstrumentSSG::getToneNoiseEnabled() const -{ - return tnEnabled_; -} - -void InstrumentSSG::setToneNoiseNumber(int n) -{ - tnNum_ = n; -} - -int InstrumentSSG::getToneNoiseNumber() const -{ - return tnNum_; -} - -std::vector InstrumentSSG::getToneNoiseSequence() const +std::vector InstrumentSSG::getToneNoiseSequence() const { return owner_->getToneNoiseSSGSequence(tnNum_); } -std::vector InstrumentSSG::getToneNoiseLoops() const +InstrumentSequenceLoopRoot InstrumentSSG::getToneNoiseLoopRoot() const { - return owner_->getToneNoiseSSGLoops(tnNum_); + return owner_->getToneNoiseSSGLoopRoot(tnNum_); } -Release InstrumentSSG::getToneNoiseRelease() const +InstrumentSequenceRelease InstrumentSSG::getToneNoiseRelease() const { return owner_->getToneNoiseSSGRelease(tnNum_); } -std::unique_ptr InstrumentSSG::getToneNoiseSequenceIterator() const +SSGToneNoiseIter InstrumentSSG::getToneNoiseSequenceIterator() const { return owner_->getToneNoiseSSGIterator(tnNum_); } -void InstrumentSSG::setEnvelopeEnabled(bool enabled) -{ - envEnabled_ = enabled; -} - -bool InstrumentSSG::getEnvelopeEnabled() const -{ - return envEnabled_; -} - -void InstrumentSSG::setEnvelopeNumber(int n) -{ - envNum_ = n; -} - -int InstrumentSSG::getEnvelopeNumber() const -{ - return envNum_; -} - -std::vector InstrumentSSG::getEnvelopeSequence() const +std::vector InstrumentSSG::getEnvelopeSequence() const { return owner_->getEnvelopeSSGSequence(envNum_); } -std::vector InstrumentSSG::getEnvelopeLoops() const +InstrumentSequenceLoopRoot InstrumentSSG::getEnvelopeLoopRoot() const { - return owner_->getEnvelopeSSGLoops(envNum_); + return owner_->getEnvelopeSSGLoopRoot(envNum_); } -Release InstrumentSSG::getEnvelopeRelease() const +InstrumentSequenceRelease InstrumentSSG::getEnvelopeRelease() const { return owner_->getEnvelopeSSGRelease(envNum_); } -std::unique_ptr InstrumentSSG::getEnvelopeSequenceIterator() const +SSGEnvelopeIter InstrumentSSG::getEnvelopeSequenceIterator() const { return owner_->getEnvelopeSSGIterator(envNum_); } -void InstrumentSSG::setArpeggioEnabled(bool enabled) -{ - arpEnabled_ = enabled; -} - -bool InstrumentSSG::getArpeggioEnabled() const -{ - return arpEnabled_; -} - -void InstrumentSSG::setArpeggioNumber(int n) -{ - arpNum_ = n; -} - -int InstrumentSSG::getArpeggioNumber() const -{ - return arpNum_; -} - SequenceType InstrumentSSG::getArpeggioType() const { return owner_->getArpeggioSSGType(arpNum_); } -std::vector InstrumentSSG::getArpeggioSequence() const +std::vector InstrumentSSG::getArpeggioSequence() const { return owner_->getArpeggioSSGSequence(arpNum_); } -std::vector InstrumentSSG::getArpeggioLoops() const +InstrumentSequenceLoopRoot InstrumentSSG::getArpeggioLoopRoot() const { - return owner_->getArpeggioSSGLoops(arpNum_); + return owner_->getArpeggioSSGLoopRoot(arpNum_); } -Release InstrumentSSG::getArpeggioRelease() const +InstrumentSequenceRelease InstrumentSSG::getArpeggioRelease() const { return owner_->getArpeggioSSGRelease(arpNum_); } -std::unique_ptr InstrumentSSG::getArpeggioSequenceIterator() const +ArpeggioIter InstrumentSSG::getArpeggioSequenceIterator() const { return owner_->getArpeggioSSGIterator(arpNum_); } -void InstrumentSSG::setPitchEnabled(bool enabled) -{ - ptEnabled_ = enabled; -} - -bool InstrumentSSG::getPitchEnabled() const -{ - return ptEnabled_; -} - -void InstrumentSSG::setPitchNumber(int n) -{ - ptNum_ = n; -} - -int InstrumentSSG::getPitchNumber() const -{ - return ptNum_; -} - SequenceType InstrumentSSG::getPitchType() const { return owner_->getPitchSSGType(ptNum_); } -std::vector InstrumentSSG::getPitchSequence() const +std::vector InstrumentSSG::getPitchSequence() const { return owner_->getPitchSSGSequence(ptNum_); } -std::vector InstrumentSSG::getPitchLoops() const +InstrumentSequenceLoopRoot InstrumentSSG::getPitchLoopRoot() const { - return owner_->getPitchSSGLoops(ptNum_); + return owner_->getPitchSSGLoopRoot(ptNum_); } -Release InstrumentSSG::getPitchRelease() const +InstrumentSequenceRelease InstrumentSSG::getPitchRelease() const { return owner_->getPitchSSGRelease(ptNum_); } -std::unique_ptr InstrumentSSG::getPitchSequenceIterator() const +PitchIter InstrumentSSG::getPitchSequenceIterator() const { return owner_->getPitchSSGIterator(ptNum_); } /****************************************/ -InstrumentADPCM::InstrumentADPCM(int number, std::string name, InstrumentsManager* owner) - : AbstractInstrument(number, name, owner), +InstrumentADPCM::InstrumentADPCM(int number, const std::string& name, InstrumentsManager* owner) + : AbstractInstrument(number, SoundSource::ADPCM, InstrumentType::ADPCM, name, owner), sampNum_(0), envEnabled_(false), envNum_(0), @@ -664,37 +472,9 @@ { } -SoundSource InstrumentADPCM::getSoundSource() const -{ - return SoundSource::ADPCM; -} - -InstrumentType InstrumentADPCM::getType() const -{ - return InstrumentType::ADPCM; -} - AbstractInstrument* InstrumentADPCM::clone() { - auto c = new InstrumentADPCM(number_, name_, owner_); - c->setSampleNumber(sampNum_); - c->setEnvelopeEnabled(envEnabled_); - c->setEnvelopeNumber(envNum_); - c->setArpeggioEnabled(arpEnabled_); - c->setArpeggioNumber(arpNum_); - c->setPitchEnabled(ptEnabled_); - c->setPitchNumber(ptNum_); - return c; -} - -void InstrumentADPCM::setSampleNumber(int n) -{ - sampNum_ = n; -} - -int InstrumentADPCM::getSampleNumber() const -{ - return sampNum_; + return new InstrumentADPCM(*this); } int InstrumentADPCM::getSampleRootKeyNumber() const @@ -727,170 +507,91 @@ return owner_->getSampleADPCMStopAddress(sampNum_); } -void InstrumentADPCM::setEnvelopeEnabled(bool enabled) -{ - envEnabled_ = enabled; -} - -bool InstrumentADPCM::getEnvelopeEnabled() const -{ - return envEnabled_; -} - -void InstrumentADPCM::setEnvelopeNumber(int n) -{ - envNum_ = n; -} - -int InstrumentADPCM::getEnvelopeNumber() const -{ - return envNum_; -} - -std::vector InstrumentADPCM::getEnvelopeSequence() const +std::vector InstrumentADPCM::getEnvelopeSequence() const { return owner_->getEnvelopeADPCMSequence(envNum_); } -std::vector InstrumentADPCM::getEnvelopeLoops() const +InstrumentSequenceLoopRoot InstrumentADPCM::getEnvelopeLoopRoot() const { - return owner_->getEnvelopeADPCMLoops(envNum_); + return owner_->getEnvelopeADPCMLoopRoot(envNum_); } -Release InstrumentADPCM::getEnvelopeRelease() const +InstrumentSequenceRelease InstrumentADPCM::getEnvelopeRelease() const { return owner_->getEnvelopeADPCMRelease(envNum_); } -std::unique_ptr InstrumentADPCM::getEnvelopeSequenceIterator() const +ADPCMEnvelopeIter InstrumentADPCM::getEnvelopeSequenceIterator() const { return owner_->getEnvelopeADPCMIterator(envNum_); } -void InstrumentADPCM::setArpeggioEnabled(bool enabled) -{ - arpEnabled_ = enabled; -} - -bool InstrumentADPCM::getArpeggioEnabled() const -{ - return arpEnabled_; -} - -void InstrumentADPCM::setArpeggioNumber(int n) -{ - arpNum_ = n; -} - -int InstrumentADPCM::getArpeggioNumber() const -{ - return arpNum_; -} - SequenceType InstrumentADPCM::getArpeggioType() const { return owner_->getArpeggioADPCMType(arpNum_); } -std::vector InstrumentADPCM::getArpeggioSequence() const +std::vector InstrumentADPCM::getArpeggioSequence() const { return owner_->getArpeggioADPCMSequence(arpNum_); } -std::vector InstrumentADPCM::getArpeggioLoops() const +InstrumentSequenceLoopRoot InstrumentADPCM::getArpeggioLoopRoot() const { - return owner_->getArpeggioADPCMLoops(arpNum_); + return owner_->getArpeggioADPCMLoopRoot(arpNum_); } -Release InstrumentADPCM::getArpeggioRelease() const +InstrumentSequenceRelease InstrumentADPCM::getArpeggioRelease() const { return owner_->getArpeggioADPCMRelease(arpNum_); } -std::unique_ptr InstrumentADPCM::getArpeggioSequenceIterator() const +ArpeggioIter InstrumentADPCM::getArpeggioSequenceIterator() const { return owner_->getArpeggioADPCMIterator(arpNum_); } -void InstrumentADPCM::setPitchEnabled(bool enabled) -{ - ptEnabled_ = enabled; -} - -bool InstrumentADPCM::getPitchEnabled() const -{ - return ptEnabled_; -} - -void InstrumentADPCM::setPitchNumber(int n) -{ - ptNum_ = n; -} - -int InstrumentADPCM::getPitchNumber() const -{ - return ptNum_; -} - SequenceType InstrumentADPCM::getPitchType() const { return owner_->getPitchADPCMType(ptNum_); } -std::vector InstrumentADPCM::getPitchSequence() const +std::vector InstrumentADPCM::getPitchSequence() const { return owner_->getPitchADPCMSequence(ptNum_); } -std::vector InstrumentADPCM::getPitchLoops() const +InstrumentSequenceLoopRoot InstrumentADPCM::getPitchLoopRoot() const { - return owner_->getPitchADPCMLoops(ptNum_); + return owner_->getPitchADPCMLoopRoot(ptNum_); } -Release InstrumentADPCM::getPitchRelease() const +InstrumentSequenceRelease InstrumentADPCM::getPitchRelease() const { return owner_->getPitchADPCMRelease(ptNum_); } -std::unique_ptr InstrumentADPCM::getPitchSequenceIterator() const +PitchIter InstrumentADPCM::getPitchSequenceIterator() const { return owner_->getPitchADPCMIterator(ptNum_); } /****************************************/ -InstrumentDrumkit::InstrumentDrumkit(int number, std::string name, InstrumentsManager* owner) - : AbstractInstrument(number, name, owner) -{ -} - -SoundSource InstrumentDrumkit::getSoundSource() const +InstrumentDrumkit::InstrumentDrumkit(int number, const std::string& name, InstrumentsManager* owner) + : AbstractInstrument(number, SoundSource::ADPCM, InstrumentType::Drumkit, name, owner) { - return SoundSource::ADPCM; -} - -InstrumentType InstrumentDrumkit::getType() const -{ - return InstrumentType::Drumkit; } AbstractInstrument* InstrumentDrumkit::clone() { - auto c = new InstrumentDrumkit(number_, name_, owner_); - - for (const auto& pair : kit_) { - c->setSampleEnabled(pair.first, true); - c->setSampleNumber(pair.first, pair.second.sampNum); - c->setPitch(pair.first, pair.second.pitch); - } - return c; + return new InstrumentDrumkit(*this); } std::vector InstrumentDrumkit::getAssignedKeys() const { - std::vector keys(kit_.size()); - std::transform(kit_.begin(), kit_.end(), keys.begin(), [](const auto& pair) { return pair.first; }); - return keys; + return utils::getMapKeys(kit_); } void InstrumentDrumkit::setSampleEnabled(int key, bool enabled) diff -Nru bambootracker-0.4.5+git20121209/BambooTracker/instrument/instrument.hpp bambootracker-0.4.6/BambooTracker/instrument/instrument.hpp --- bambootracker-0.4.5+git20121209/BambooTracker/instrument/instrument.hpp 2020-12-08 14:05:28.000000000 +0000 +++ bambootracker-0.4.6/BambooTracker/instrument/instrument.hpp 2021-02-11 14:37:17.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (C) 2018-2020 Rerrah + * Copyright (C) 2018-2021 Rerrah * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation @@ -29,12 +29,12 @@ #include #include #include -#include "instruments_manager.hpp" #include "envelope_fm.hpp" #include "lfo_fm.hpp" -#include "command_sequence.hpp" +#include "sequence_property.hpp" +#include "instrument_property_defs.hpp" #include "enum_hash.hpp" -#include "misc.hpp" +#include "bamboo_tracker_defs.hpp" class InstrumentsManager; @@ -45,12 +45,12 @@ public: virtual ~AbstractInstrument() = default; - int getNumber() const; - void setNumber(int n); - virtual SoundSource getSoundSource() const = 0; - virtual InstrumentType getType() const = 0; - std::string getName() const; - void setName(std::string name); + inline int getNumber() const noexcept { return number_; } + inline void setNumber(int n) noexcept { number_ = n; } + inline SoundSource getSoundSource() const noexcept { return sndSrc_; } + inline InstrumentType getType() const noexcept { return instType_; } + inline std::string getName() const noexcept { return name_; } + inline void setName(const std::string& name) { name_ = name; } bool isRegisteredWithManager() const; virtual AbstractInstrument* clone() = 0; @@ -58,57 +58,59 @@ InstrumentsManager* owner_; int number_; std::string name_; // UTF-8 - AbstractInstrument(int number, std::string name, InstrumentsManager* owner); + AbstractInstrument(int number, SoundSource src, InstrumentType type, const std::string& name, InstrumentsManager* owner); + +private: + const SoundSource sndSrc_; + const InstrumentType instType_; }; -class InstrumentFM : public AbstractInstrument +class InstrumentFM final : public AbstractInstrument { public: - InstrumentFM(int number, std::string name, InstrumentsManager* owner); - SoundSource getSoundSource() const override; - InstrumentType getType() const override; + InstrumentFM(int number, const std::string& name, InstrumentsManager* owner); AbstractInstrument* clone() override; - void setEnvelopeNumber(int n); - int getEnvelopeNumber() const; + inline void setEnvelopeNumber(int n) noexcept { envNum_ = n; } + inline int getEnvelopeNumber() const noexcept { return envNum_; } int getEnvelopeParameter(FMEnvelopeParameter param) const; bool getOperatorEnabled(int n) const; - void setLFOEnabled(bool enabled); - bool getLFOEnabled() const; - void setLFONumber(int n); - int getLFONumber() const; + inline void setLFOEnabled(bool enabled) noexcept { lfoEnabled_ = enabled; } + inline bool getLFOEnabled() const noexcept { return lfoEnabled_; } + inline void setLFONumber(int n) noexcept { lfoNum_ = n; } + inline int getLFONumber() const noexcept { return lfoNum_; } int getLFOParameter(FMLFOParameter param) const; void setOperatorSequenceEnabled(FMEnvelopeParameter param, bool enabled); bool getOperatorSequenceEnabled(FMEnvelopeParameter param) const; void setOperatorSequenceNumber(FMEnvelopeParameter param, int n); int getOperatorSequenceNumber(FMEnvelopeParameter param) const; - std::vector getOperatorSequenceSequence(FMEnvelopeParameter param) const; - std::vector getOperatorSequenceLoops(FMEnvelopeParameter param) const; - Release getOperatorSequenceRelease(FMEnvelopeParameter param) const; - std::unique_ptr getOperatorSequenceSequenceIterator(FMEnvelopeParameter param) const; + std::vector getOperatorSequenceSequence(FMEnvelopeParameter param) const; + InstrumentSequenceLoopRoot getOperatorSequenceLoopRoot(FMEnvelopeParameter param) const; + InstrumentSequenceRelease getOperatorSequenceRelease(FMEnvelopeParameter param) const; + FMOperatorSequenceIter getOperatorSequenceSequenceIterator(FMEnvelopeParameter param) const; void setArpeggioEnabled(FMOperatorType op, bool enabled); bool getArpeggioEnabled(FMOperatorType op) const; void setArpeggioNumber(FMOperatorType op, int n); int getArpeggioNumber(FMOperatorType op) const; SequenceType getArpeggioType(FMOperatorType op) const; - std::vector getArpeggioSequence(FMOperatorType op) const; - std::vector getArpeggioLoops(FMOperatorType op) const; - Release getArpeggioRelease(FMOperatorType op) const; - std::unique_ptr getArpeggioSequenceIterator(FMOperatorType op) const; + std::vector getArpeggioSequence(FMOperatorType op) const; + InstrumentSequenceLoopRoot getArpeggioLoopRoot(FMOperatorType op) const; + InstrumentSequenceRelease getArpeggioRelease(FMOperatorType op) const; + ArpeggioIter getArpeggioSequenceIterator(FMOperatorType op) const; void setPitchEnabled(FMOperatorType op, bool enabled); bool getPitchEnabled(FMOperatorType op) const; void setPitchNumber(FMOperatorType op, int n); int getPitchNumber(FMOperatorType op) const; SequenceType getPitchType(FMOperatorType op) const; - std::vector getPitchSequence(FMOperatorType op) const; - std::vector getPitchLoops(FMOperatorType op) const; - Release getPitchRelease(FMOperatorType op) const; - std::unique_ptr getPitchSequenceIterator(FMOperatorType op) const; + std::vector getPitchSequence(FMOperatorType op) const; + InstrumentSequenceLoopRoot getPitchLoopRoot(FMOperatorType op) const; + InstrumentSequenceRelease getPitchRelease(FMOperatorType op) const; + PitchIter getPitchSequenceIterator(FMOperatorType op) const; void setEnvelopeResetEnabled(FMOperatorType op, bool enabled); bool getEnvelopeResetEnabled(FMOperatorType op) const; @@ -128,60 +130,58 @@ }; -class InstrumentSSG : public AbstractInstrument +class InstrumentSSG final : public AbstractInstrument { public: - InstrumentSSG(int number, std::string name, InstrumentsManager* owner); - SoundSource getSoundSource() const override; - InstrumentType getType() const override; + InstrumentSSG(int number, const std::string& name, InstrumentsManager* owner); AbstractInstrument* clone() override; - void setWaveformEnabled(bool enabled); - bool getWaveformEnabled() const; - void setWaveformNumber(int n); - int getWaveformNumber() const; - std::vector getWaveformSequence() const; - std::vector getWaveformLoops() const; - Release getWaveformRelease() const; - std::unique_ptr getWaveformSequenceIterator() const; - - void setToneNoiseEnabled(bool enabled); - bool getToneNoiseEnabled() const; - void setToneNoiseNumber(int n); - int getToneNoiseNumber() const; - std::vector getToneNoiseSequence() const; - std::vector getToneNoiseLoops() const; - Release getToneNoiseRelease() const; - std::unique_ptr getToneNoiseSequenceIterator() const; - - void setEnvelopeEnabled(bool enabled); - bool getEnvelopeEnabled() const; - void setEnvelopeNumber(int n); - int getEnvelopeNumber() const; - std::vector getEnvelopeSequence() const; - std::vector getEnvelopeLoops() const; - Release getEnvelopeRelease() const; - std::unique_ptr getEnvelopeSequenceIterator() const; - - void setArpeggioEnabled(bool enabled); - bool getArpeggioEnabled() const; - void setArpeggioNumber(int n); - int getArpeggioNumber() const; + inline void setWaveformEnabled(bool enabled) noexcept { wfEnabled_ = enabled; } + inline bool getWaveformEnabled() const noexcept { return wfEnabled_; } + inline void setWaveformNumber(int n) noexcept { wfNum_ = n; } + inline int getWaveformNumber() const noexcept { return wfNum_; } + std::vector getWaveformSequence() const; + InstrumentSequenceLoopRoot getWaveformLoopRoot() const; + InstrumentSequenceRelease getWaveformRelease() const; + SSGWaveformIter getWaveformSequenceIterator() const; + + inline void setToneNoiseEnabled(bool enabled) noexcept { tnEnabled_ = enabled; } + inline bool getToneNoiseEnabled() const noexcept { return tnEnabled_; } + inline void setToneNoiseNumber(int n) noexcept { tnNum_ = n; } + inline int getToneNoiseNumber() const noexcept { return tnNum_; } + std::vector getToneNoiseSequence() const; + InstrumentSequenceLoopRoot getToneNoiseLoopRoot() const; + InstrumentSequenceRelease getToneNoiseRelease() const; + SSGToneNoiseIter getToneNoiseSequenceIterator() const; + + inline void setEnvelopeEnabled(bool enabled) noexcept { envEnabled_ = enabled; } + inline bool getEnvelopeEnabled() const noexcept { return envEnabled_; } + inline void setEnvelopeNumber(int n) noexcept { envNum_ = n; } + inline int getEnvelopeNumber() const noexcept { return envNum_; } + std::vector getEnvelopeSequence() const; + InstrumentSequenceLoopRoot getEnvelopeLoopRoot() const; + InstrumentSequenceRelease getEnvelopeRelease() const; + SSGEnvelopeIter getEnvelopeSequenceIterator() const; + + inline void setArpeggioEnabled(bool enabled) noexcept { arpEnabled_ = enabled; } + inline bool getArpeggioEnabled() const noexcept { return arpEnabled_; } + inline void setArpeggioNumber(int n) noexcept { arpNum_ = n; } + inline int getArpeggioNumber() const noexcept { return arpNum_; } SequenceType getArpeggioType() const; - std::vector getArpeggioSequence() const; - std::vector getArpeggioLoops() const; - Release getArpeggioRelease() const; - std::unique_ptr getArpeggioSequenceIterator() const; - - void setPitchEnabled(bool enabled); - bool getPitchEnabled() const; - void setPitchNumber(int n); - int getPitchNumber() const; + std::vector getArpeggioSequence() const; + InstrumentSequenceLoopRoot getArpeggioLoopRoot() const; + InstrumentSequenceRelease getArpeggioRelease() const; + ArpeggioIter getArpeggioSequenceIterator() const; + + inline void setPitchEnabled(bool enabled) noexcept { ptEnabled_ = enabled; } + inline bool getPitchEnabled() const noexcept { return ptEnabled_; } + inline void setPitchNumber(int n) noexcept { ptNum_ = n; } + inline int getPitchNumber() const noexcept { return ptNum_; } SequenceType getPitchType() const; - std::vector getPitchSequence() const; - std::vector getPitchLoops() const; - Release getPitchRelease() const; - std::unique_ptr getPitchSequenceIterator() const; + std::vector getPitchSequence() const; + InstrumentSequenceLoopRoot getPitchLoopRoot() const; + InstrumentSequenceRelease getPitchRelease() const; + PitchIter getPitchSequenceIterator() const; private: bool wfEnabled_; @@ -197,16 +197,14 @@ }; -class InstrumentADPCM : public AbstractInstrument +class InstrumentADPCM final : public AbstractInstrument { public: - InstrumentADPCM(int number, std::string name, InstrumentsManager* owner); - SoundSource getSoundSource() const override; - InstrumentType getType() const override; + InstrumentADPCM(int number, const std::string& name, InstrumentsManager* owner); AbstractInstrument* clone() override; - void setSampleNumber(int n); - int getSampleNumber() const; + inline void setSampleNumber(int n) noexcept { sampNum_ = n; } + inline int getSampleNumber() const noexcept { return sampNum_; } int getSampleRootKeyNumber() const; int getSampleRootDeltaN() const; bool isSampleRepeatable() const; @@ -214,34 +212,34 @@ size_t getSampleStartAddress() const; size_t getSampleStopAddress() const; - void setEnvelopeEnabled(bool enabled); - bool getEnvelopeEnabled() const; - void setEnvelopeNumber(int n); - int getEnvelopeNumber() const; - std::vector getEnvelopeSequence() const; - std::vector getEnvelopeLoops() const; - Release getEnvelopeRelease() const; - std::unique_ptr getEnvelopeSequenceIterator() const; - - void setArpeggioEnabled(bool enabled); - bool getArpeggioEnabled() const; - void setArpeggioNumber(int n); - int getArpeggioNumber() const; + inline void setEnvelopeEnabled(bool enabled) noexcept { envEnabled_ = enabled; } + inline bool getEnvelopeEnabled() const noexcept { return envEnabled_; } + inline void setEnvelopeNumber(int n) noexcept { envNum_ = n; } + inline int getEnvelopeNumber() const noexcept { return envNum_; } + std::vector getEnvelopeSequence() const; + InstrumentSequenceLoopRoot getEnvelopeLoopRoot() const; + InstrumentSequenceRelease getEnvelopeRelease() const; + ADPCMEnvelopeIter getEnvelopeSequenceIterator() const; + + inline void setArpeggioEnabled(bool enabled) noexcept { arpEnabled_ = enabled; } + inline bool getArpeggioEnabled() const noexcept { return arpEnabled_; } + inline void setArpeggioNumber(int n) noexcept { arpNum_ = n; } + inline int getArpeggioNumber() const noexcept { return arpNum_; } SequenceType getArpeggioType() const; - std::vector getArpeggioSequence() const; - std::vector getArpeggioLoops() const; - Release getArpeggioRelease() const; - std::unique_ptr getArpeggioSequenceIterator() const; - - void setPitchEnabled(bool enabled); - bool getPitchEnabled() const; - void setPitchNumber(int n); - int getPitchNumber() const; + std::vector getArpeggioSequence() const; + InstrumentSequenceLoopRoot getArpeggioLoopRoot() const; + InstrumentSequenceRelease getArpeggioRelease() const; + ArpeggioIter getArpeggioSequenceIterator() const; + + inline void setPitchEnabled(bool enabled) noexcept { ptEnabled_ = enabled; } + inline bool getPitchEnabled() const noexcept { return ptEnabled_; } + inline void setPitchNumber(int n) noexcept { ptNum_ = n; } + inline int getPitchNumber() const noexcept { return ptNum_; } SequenceType getPitchType() const; - std::vector getPitchSequence() const; - std::vector getPitchLoops() const; - Release getPitchRelease() const; - std::unique_ptr getPitchSequenceIterator() const; + std::vector getPitchSequence() const; + InstrumentSequenceLoopRoot getPitchLoopRoot() const; + InstrumentSequenceRelease getPitchRelease() const; + PitchIter getPitchSequenceIterator() const; private: int sampNum_; @@ -254,12 +252,10 @@ }; -class InstrumentDrumkit : public AbstractInstrument +class InstrumentDrumkit final : public AbstractInstrument { public: - InstrumentDrumkit(int number, std::string name, InstrumentsManager* owner); - SoundSource getSoundSource() const override; - InstrumentType getType() const override; + InstrumentDrumkit(int number, const std::string& name, InstrumentsManager* owner); AbstractInstrument* clone() override; std::vector getAssignedKeys() const; diff -Nru bambootracker-0.4.5+git20121209/BambooTracker/instrument/instrument_property_defs.hpp bambootracker-0.4.6/BambooTracker/instrument/instrument_property_defs.hpp --- bambootracker-0.4.5+git20121209/BambooTracker/instrument/instrument_property_defs.hpp 1970-01-01 00:00:00.000000000 +0000 +++ bambootracker-0.4.6/BambooTracker/instrument/instrument_property_defs.hpp 2021-02-11 14:37:17.000000000 +0000 @@ -0,0 +1,84 @@ +/* + * Copyright (C) 2021 Rerrah + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +#pragma once + +#include "sequence_property.hpp" + +using FMOperatorSequenceUnit = InstrumentSequenceBaseUnit; +using FMOperatorSequenceIter = std::unique_ptr::Iterator>; + +enum class FMOperatorType +{ + All, Op1, Op2, Op3, Op4 +}; + +using ArpeggioUnit = InstrumentSequenceBaseUnit; +using ArpeggioIter = std::unique_ptr::Iterator>; + +using PitchUnit = InstrumentSequenceBaseUnit; +using PitchIter = std::unique_ptr::Iterator>; + +constexpr int SEQ_PITCH_CENTER = 127; + +using SSGWaveformUnit = InstrumentSequenceExtendUnit; +using SSGWaveformIter = std::unique_ptr::Iterator>; + +class SSGWaveformType { +public: + enum : int + { + UNSET = SSGWaveformUnit::ERR_DATA, + SQUARE = 0, + TRIANGLE = 1, + SAW = 2, + INVSAW = 3, + SQM_TRIANGLE = 4, + SQM_SAW = 5, + SQM_INVSAW = 6 + }; + + static bool testHardEnvelopeOccupancity(int form) + { + switch (form) { + case SSGWaveformType::UNSET: + case SSGWaveformType::SQUARE: + return false; + default: + return true; + } + } + + SSGWaveformType() = delete; +}; + +using SSGToneNoiseUnit = InstrumentSequenceBaseUnit; +using SSGToneNoiseIter = std::unique_ptr::Iterator>; + +using SSGEnvelopeUnit = InstrumentSequenceExtendUnit; +using SSGEnvelopeIter = std::unique_ptr::Iterator>; + +using ADPCMEnvelopeUnit = InstrumentSequenceBaseUnit; +using ADPCMEnvelopeIter = std::unique_ptr::Iterator>; diff -Nru bambootracker-0.4.5+git20121209/BambooTracker/instrument/instruments_manager.cpp bambootracker-0.4.6/BambooTracker/instrument/instruments_manager.cpp --- bambootracker-0.4.5+git20121209/BambooTracker/instrument/instruments_manager.cpp 2020-12-08 14:05:28.000000000 +0000 +++ bambootracker-0.4.6/BambooTracker/instrument/instruments_manager.cpp 2021-02-11 14:37:17.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (C) 2018-2020 Rerrah + * Copyright (C) 2018-2021 Rerrah * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation @@ -27,72 +27,170 @@ #include #include #include -#include "instrument.hpp" #include +#include +#include +#include "instrument.hpp" +#include "note.hpp" +#include "utils.hpp" -const FMEnvelopeParameter InstrumentsManager::ENV_FM_PARAMS_[38] = { - FMEnvelopeParameter::AL, - FMEnvelopeParameter::FB, - FMEnvelopeParameter::AR1, - FMEnvelopeParameter::DR1, - FMEnvelopeParameter::SR1, - FMEnvelopeParameter::RR1, - FMEnvelopeParameter::SL1, - FMEnvelopeParameter::TL1, - FMEnvelopeParameter::KS1, - FMEnvelopeParameter::ML1, - FMEnvelopeParameter::DT1, - FMEnvelopeParameter::AR2, - FMEnvelopeParameter::DR2, - FMEnvelopeParameter::SR2, - FMEnvelopeParameter::RR2, - FMEnvelopeParameter::SL2, - FMEnvelopeParameter::TL2, - FMEnvelopeParameter::KS2, - FMEnvelopeParameter::ML2, - FMEnvelopeParameter::DT2, - FMEnvelopeParameter::AR3, - FMEnvelopeParameter::DR3, - FMEnvelopeParameter::SR3, - FMEnvelopeParameter::RR3, - FMEnvelopeParameter::SL3, - FMEnvelopeParameter::TL3, - FMEnvelopeParameter::KS3, - FMEnvelopeParameter::ML3, - FMEnvelopeParameter::DT3, - FMEnvelopeParameter::AR4, - FMEnvelopeParameter::DR4, - FMEnvelopeParameter::SR4, - FMEnvelopeParameter::RR4, - FMEnvelopeParameter::SL4, - FMEnvelopeParameter::TL4, - FMEnvelopeParameter::KS4, - FMEnvelopeParameter::ML4, - FMEnvelopeParameter::DT4 +namespace +{ +// Constant +const FMEnvelopeParameter ENV_FM_PARAMS[] = { + FMEnvelopeParameter::AL, FMEnvelopeParameter::FB, FMEnvelopeParameter::AR1, + FMEnvelopeParameter::DR1, FMEnvelopeParameter::SR1, FMEnvelopeParameter::RR1, + FMEnvelopeParameter::SL1, FMEnvelopeParameter::TL1, FMEnvelopeParameter::KS1, + FMEnvelopeParameter::ML1, FMEnvelopeParameter::DT1, FMEnvelopeParameter::AR2, + FMEnvelopeParameter::DR2, FMEnvelopeParameter::SR2, FMEnvelopeParameter::RR2, + FMEnvelopeParameter::SL2, FMEnvelopeParameter::TL2, FMEnvelopeParameter::KS2, + FMEnvelopeParameter::ML2, FMEnvelopeParameter::DT2, FMEnvelopeParameter::AR3, + FMEnvelopeParameter::DR3, FMEnvelopeParameter::SR3, FMEnvelopeParameter::RR3, + FMEnvelopeParameter::SL3, FMEnvelopeParameter::TL3, FMEnvelopeParameter::KS3, + FMEnvelopeParameter::ML3, FMEnvelopeParameter::DT3, FMEnvelopeParameter::AR4, + FMEnvelopeParameter::DR4, FMEnvelopeParameter::SR4, FMEnvelopeParameter::RR4, + FMEnvelopeParameter::SL4, FMEnvelopeParameter::TL4, FMEnvelopeParameter::KS4, + FMEnvelopeParameter::ML4, FMEnvelopeParameter::DT4 }; + +const FMOperatorType FM_OP_TYPES[] = { + FMOperatorType::All, FMOperatorType::Op1, FMOperatorType::Op2, + FMOperatorType::Op3, FMOperatorType::Op4 }; -const FMOperatorType InstrumentsManager::FM_OP_TYPES_[5] = { - FMOperatorType::All, - FMOperatorType::Op1, - FMOperatorType::Op2, - FMOperatorType::Op3, - FMOperatorType::Op4 +// Property initialization helper +inline auto makeEnvelopeFMSharedPtr(int n) +{ + return std::make_shared(n); +} + +inline auto makeLFOFMSharedPtr(int n) +{ + return std::make_shared(n); +} + +inline auto makeOperatorSequenceFMSharedPtr(int n) +{ + return std::make_shared>(n, SequenceType::PlainSequence, FMOperatorSequenceUnit(0), FMOperatorSequenceUnit()); +} + +inline auto makeArpeggioSharedPtr(int n) +{ + return std::make_shared>(n, SequenceType::AbsoluteSequence, ArpeggioUnit(Note::DEFAULT_NOTE_NUM), ArpeggioUnit()); +} + +inline auto makePitchSharedPtr(int n) +{ + return std::make_shared>(n, SequenceType::AbsoluteSequence, PitchUnit(SEQ_PITCH_CENTER), PitchUnit()); +} + +inline auto makeWaveformSSGSharedPtr(int n) +{ + return std::make_shared>(n, SequenceType::PlainSequence, + SSGWaveformUnit::makeOnlyDataUnit(SSGWaveformType::SQUARE), + SSGWaveformUnit()); +} + +inline auto makeToneNoiseSSGSharedPtr(int n) +{ + return std::make_shared>(n, SequenceType::PlainSequence, SSGToneNoiseUnit(0), SSGToneNoiseUnit()); +} + +inline auto makeEnvelopeSSGSharedPtr(int n) +{ + return std::make_shared>(n, SequenceType::PlainSequence, + SSGEnvelopeUnit::makeOnlyDataUnit(15), SSGEnvelopeUnit(), 15); +} + +inline auto makeSampleADPCMSharedPtr(int n) +{ + return std::make_shared(n); +} + +inline auto makeEnvelopeADPCMSharedPtr(int n) +{ + return std::make_shared>(n, SequenceType::PlainSequence, + ADPCMEnvelopeUnit(255), ADPCMEnvelopeUnit(), 127); +} + +// Utility functor +template +struct IsUsed +{ + using ArgType = typename std::add_const::type::const_reference; + bool operator()(ArgType prop) const + { + return prop->isUserInstrument(); + } }; +template +struct IsEdited +{ + using ArgType = typename std::add_const::type::const_reference; + bool operator()(ArgType prop) const + { + return prop->isEdited(); + } +}; + +template +struct IsUsedOrEdited +{ + using ArgType = typename std::add_const::type::const_reference; + bool operator()(ArgType prop) const + { + return (prop->isUserInstrument() || prop->isEdited()); + } +}; + +// Common process for properties +template +int cloneProperty(propArray& aryRef, int srcNum) +{ + // Must find an unused property since the count of used properties <= the count of instruments + auto&& it = std::find_if_not(aryRef.begin(), aryRef.end(), IsUsed()); + int cloneNum = std::distance(aryRef.begin(), it); + *it = aryRef.at(static_cast(srcNum))->clone(); + (*it)->setNumber(cloneNum); + return cloneNum; +} + +template +int findFirstAssignableProperty(propArray& aryRef, bool regardingUnedited, int startOffs = 0) +{ + std::function cond; + if (regardingUnedited) cond = IsUsedOrEdited(); + else cond = IsUsed(); + auto&& it = std::find_if_not(aryRef.cbegin() + startOffs, aryRef.cend(), cond); + + if (it == aryRef.cend()) return -1; + + if (!regardingUnedited) (*it)->clearParameters(); + return std::distance(aryRef.cbegin(), it); +} +} + InstrumentsManager::InstrumentsManager(bool unedited) : regardingUnedited_(unedited) { clearAll(); } -void InstrumentsManager::addInstrument(int instNum, InstrumentType type, std::string name) +void InstrumentsManager::addInstrument(int instNum, InstrumentType type, const std::string& name) { if (instNum < 0 || static_cast(insts_.size()) <= instNum) return; switch (type) { case InstrumentType::FM: { - auto fm = std::make_shared(instNum, name, this); + auto fm = new InstrumentFM(instNum, name, this); int envNum = findFirstAssignableEnvelopeFM(); if (envNum == -1) envNum = static_cast(envFM_.size()) - 1; fm->setEnvelopeNumber(envNum); @@ -101,7 +199,7 @@ if (lfoNum == -1) lfoNum = static_cast(lfoFM_.size()) - 1; fm->setLFONumber(lfoNum); fm->setLFOEnabled(false); - for (auto param : ENV_FM_PARAMS_) { + for (auto param : ENV_FM_PARAMS) { int opSeqNum = findFirstAssignableOperatorSequenceFM(param); if (opSeqNum == -1) opSeqNum = static_cast(opSeqFM_.at(param).size()) - 1; fm->setOperatorSequenceNumber(param, opSeqNum); @@ -111,18 +209,18 @@ if (arpNum == -1) arpNum = static_cast(arpFM_.size()) - 1; int ptNum = findFirstAssignablePitchFM(); if (ptNum == -1) ptNum = static_cast(ptFM_.size()) - 1; - for (auto type : FM_OP_TYPES_) { + for (auto type : FM_OP_TYPES) { fm->setArpeggioNumber(type, arpNum); fm->setArpeggioEnabled(type, false); fm->setPitchNumber(type, ptNum); fm->setPitchEnabled(type, false); } - insts_.at(static_cast(instNum)) = std::move(fm); + insts_.at(static_cast(instNum)).reset(fm); break; } case InstrumentType::SSG: { - auto ssg = std::make_shared(instNum, name, this); + auto ssg = new InstrumentSSG(instNum, name, this); int wfNum = findFirstAssignableWaveformSSG(); if (wfNum == -1) wfNum = static_cast(wfSSG_.size()) - 1; ssg->setWaveformNumber(wfNum); @@ -143,12 +241,12 @@ if (ptNum == -1) ptNum = static_cast(ptSSG_.size()) - 1; ssg->setPitchNumber(ptNum); ssg->setPitchEnabled(false); - insts_.at(static_cast(instNum)) = std::move(ssg); + insts_.at(static_cast(instNum)).reset(ssg); break; } case InstrumentType::ADPCM: { - auto adpcm = std::make_shared(instNum, name, this); + auto adpcm = new InstrumentADPCM(instNum, name, this); int sampNum = findFirstAssignableSampleADPCM(); if (sampNum == -1) sampNum = static_cast(sampADPCM_.size()) - 1; adpcm->setSampleNumber(sampNum); @@ -165,13 +263,12 @@ if (ptNum == -1) ptNum = static_cast(ptADPCM_.size()) - 1; adpcm->setPitchNumber(ptNum); adpcm->setPitchEnabled(false); - insts_.at(static_cast(instNum)) = std::move(adpcm); + insts_.at(static_cast(instNum)).reset(adpcm); break; } case InstrumentType::Drumkit: { - auto kit = std::make_shared(instNum, name, this); - insts_.at(static_cast(instNum)) = std::move(kit); + insts_.at(static_cast(instNum)).reset(new InstrumentDrumkit(instNum, name, this)); break; } default: @@ -179,23 +276,24 @@ } } -void InstrumentsManager::addInstrument(std::unique_ptr inst) +void InstrumentsManager::addInstrument(AbstractInstrument* newInstPtr) { - int num = inst->getNumber(); - insts_.at(static_cast(num)) = std::move(inst); + int num = newInstPtr->getNumber(); + std::shared_ptr& inst = insts_.at(static_cast(num)); + inst.reset(newInstPtr); - switch (insts_[static_cast(num)]->getType()) { + switch (inst->getType()) { case InstrumentType::FM: { - auto fm = std::dynamic_pointer_cast(insts_[static_cast(num)]); + auto fm = std::dynamic_pointer_cast(inst); envFM_.at(static_cast(fm->getEnvelopeNumber()))->registerUserInstrument(num); if (fm->getLFOEnabled()) lfoFM_.at(static_cast(fm->getLFONumber()))->registerUserInstrument(num); - for (auto p : ENV_FM_PARAMS_) { + for (auto p : ENV_FM_PARAMS) { if (fm->getOperatorSequenceEnabled(p)) opSeqFM_.at(p).at(static_cast(fm->getOperatorSequenceNumber(p))) ->registerUserInstrument(num); } - for (auto t : FM_OP_TYPES_) { + for (auto t : FM_OP_TYPES) { if (fm->getArpeggioEnabled(t)) arpFM_.at(static_cast(fm->getArpeggioNumber(t)))->registerUserInstrument(num); if (fm->getPitchEnabled(t)) @@ -205,7 +303,7 @@ } case InstrumentType::SSG: { - auto ssg = std::dynamic_pointer_cast(insts_[static_cast(num)]); + auto ssg = std::dynamic_pointer_cast(inst); if (ssg->getWaveformEnabled()) wfSSG_.at(static_cast(ssg->getWaveformNumber()))->registerUserInstrument(num); if (ssg->getToneNoiseEnabled()) @@ -220,7 +318,7 @@ } case InstrumentType::ADPCM: { - auto adpcm = std::dynamic_pointer_cast(insts_[static_cast(num)]); + auto adpcm = std::dynamic_pointer_cast(inst); sampADPCM_.at(static_cast(adpcm->getSampleNumber()))->registerUserInstrument(num); if (adpcm->getEnvelopeEnabled()) envADPCM_.at(static_cast(adpcm->getEnvelopeNumber()))->registerUserInstrument(num); @@ -232,7 +330,7 @@ } case InstrumentType::Drumkit: { - auto kit = std::dynamic_pointer_cast(insts_[static_cast(num)]); + auto kit = std::dynamic_pointer_cast(inst); for (const auto& key : kit->getAssignedKeys()) { sampADPCM_.at(static_cast(kit->getSampleNumber(key)))->registerUserInstrument(num); } @@ -243,9 +341,76 @@ } } +std::unique_ptr InstrumentsManager::removeInstrument(int instNum) +{ + std::shared_ptr& inst = insts_.at(static_cast(instNum)); + switch (inst->getType()) { + case InstrumentType::FM: + { + auto fm = std::dynamic_pointer_cast(inst); + envFM_.at(static_cast(fm->getEnvelopeNumber()))->deregisterUserInstrument(instNum); + if (fm->getLFOEnabled()) + lfoFM_.at(static_cast(fm->getLFONumber()))->deregisterUserInstrument(instNum); + for (auto p : ENV_FM_PARAMS) { + if (fm->getOperatorSequenceEnabled(p)) + opSeqFM_.at(p).at(static_cast(fm->getOperatorSequenceNumber(p))) + ->deregisterUserInstrument(instNum); + } + for (auto t : FM_OP_TYPES) { + if (fm->getArpeggioEnabled(t)) + arpFM_.at(static_cast(fm->getArpeggioNumber(t)))->deregisterUserInstrument(instNum); + if (fm->getPitchEnabled(t)) + ptFM_.at(static_cast(fm->getPitchNumber(t)))->deregisterUserInstrument(instNum); + } + break; + } + case InstrumentType::SSG: + { + auto ssg = std::dynamic_pointer_cast(inst); + if (ssg->getWaveformEnabled()) + wfSSG_.at(static_cast(ssg->getWaveformNumber()))->deregisterUserInstrument(instNum); + if (ssg->getToneNoiseEnabled()) + tnSSG_.at(static_cast(ssg->getToneNoiseNumber()))->deregisterUserInstrument(instNum); + if (ssg->getEnvelopeEnabled()) + envSSG_.at(static_cast(ssg->getEnvelopeNumber()))->deregisterUserInstrument(instNum); + if (ssg->getArpeggioEnabled()) + arpSSG_.at(static_cast(ssg->getArpeggioNumber()))->deregisterUserInstrument(instNum); + if (ssg->getPitchEnabled()) + ptSSG_.at(static_cast(ssg->getPitchNumber()))->deregisterUserInstrument(instNum); + break; + } + case InstrumentType::ADPCM: + { + auto adpcm = std::dynamic_pointer_cast(inst); + sampADPCM_.at(static_cast(adpcm->getSampleNumber()))->deregisterUserInstrument(instNum); + if (adpcm->getEnvelopeEnabled()) + envADPCM_.at(static_cast(adpcm->getEnvelopeNumber()))->deregisterUserInstrument(instNum); + if (adpcm->getArpeggioEnabled()) + arpADPCM_.at(static_cast(adpcm->getArpeggioNumber()))->deregisterUserInstrument(instNum); + if (adpcm->getPitchEnabled()) + ptADPCM_.at(static_cast(adpcm->getPitchNumber()))->deregisterUserInstrument(instNum); + break; + } + case InstrumentType::Drumkit: + { + auto kit = std::dynamic_pointer_cast(inst); + for (const int& key : kit->getAssignedKeys()) { + sampADPCM_.at(static_cast(kit->getSampleNumber(key)))->deregisterUserInstrument(instNum); + } + break; + } + default: + throw std::invalid_argument("invalid instrument type"); + } + + std::unique_ptr clone(inst->clone()); + inst.reset(); + return clone; +} + void InstrumentsManager::cloneInstrument(int cloneInstNum, int refInstNum) { - std::shared_ptr refInst = insts_.at(static_cast(refInstNum)); + std::shared_ptr& refInst = insts_.at(static_cast(refInstNum)); addInstrument(cloneInstNum, refInst->getType(), refInst->getName()); switch (refInst->getType()) { @@ -256,11 +421,11 @@ setInstrumentFMEnvelope(cloneInstNum, refFm->getEnvelopeNumber()); setInstrumentFMLFO(cloneInstNum, refFm->getLFONumber()); if (refFm->getLFOEnabled()) setInstrumentFMLFOEnabled(cloneInstNum, true); - for (auto p : ENV_FM_PARAMS_) { + for (auto p : ENV_FM_PARAMS) { setInstrumentFMOperatorSequence(cloneInstNum, p, refFm->getOperatorSequenceNumber(p)); if (refFm->getOperatorSequenceEnabled(p)) setInstrumentFMOperatorSequenceEnabled(cloneInstNum, p, true); } - for (auto t : FM_OP_TYPES_) { + for (auto t : FM_OP_TYPES) { setInstrumentFMArpeggio(cloneInstNum, t, refFm->getArpeggioNumber(t)); if (refFm->getArpeggioEnabled(t)) setInstrumentFMArpeggioEnabled(cloneInstNum, t, true); setInstrumentFMPitch(cloneInstNum, t, refFm->getPitchNumber(t)); @@ -315,7 +480,7 @@ void InstrumentsManager::deepCloneInstrument(int cloneInstNum, int refInstNum) { - std::shared_ptr refInst = insts_.at(static_cast(refInstNum)); + std::shared_ptr& refInst = insts_.at(static_cast(refInstNum)); addInstrument(cloneInstNum, refInst->getType(), refInst->getName()); switch (refInst->getType()) { @@ -325,58 +490,52 @@ auto cloneFm = std::dynamic_pointer_cast(insts_.at(static_cast(cloneInstNum))); envFM_[static_cast(cloneFm->getEnvelopeNumber())]->deregisterUserInstrument(cloneInstNum); // Remove temporary number - int envNum = cloneFMEnvelope(refFm->getEnvelopeNumber()); + int envNum = cloneProperty(envFM_, refFm->getEnvelopeNumber()); cloneFm->setEnvelopeNumber(envNum); envFM_[static_cast(envNum)]->registerUserInstrument(cloneInstNum); if (refFm->getLFOEnabled()) { cloneFm->setLFOEnabled(true); - int lfoNum = cloneFMLFO(refFm->getLFONumber()); + int lfoNum = cloneProperty(lfoFM_, refFm->getLFONumber()); cloneFm->setLFONumber(lfoNum); lfoFM_[static_cast(lfoNum)]->registerUserInstrument(cloneInstNum); } - for (auto p : ENV_FM_PARAMS_) { - if (refFm->getOperatorSequenceEnabled(p)) { - cloneFm->setOperatorSequenceEnabled(p, true); - int opSeqNum = cloneFMOperatorSequence(p, refFm->getOperatorSequenceNumber(p)); - cloneFm->setOperatorSequenceNumber(p, opSeqNum); - opSeqFM_.at(p)[static_cast(opSeqNum)]->registerUserInstrument(cloneInstNum); + for (auto envParam : ENV_FM_PARAMS) { + if (refFm->getOperatorSequenceEnabled(envParam)) { + cloneFm->setOperatorSequenceEnabled(envParam, true); + int opSeqNum = cloneProperty(opSeqFM_.at(envParam), refFm->getOperatorSequenceNumber(envParam)); + cloneFm->setOperatorSequenceNumber(envParam, opSeqNum); + opSeqFM_.at(envParam)[static_cast(opSeqNum)]->registerUserInstrument(cloneInstNum); } } - std::unordered_map arpNums; - for (auto t : FM_OP_TYPES_) { - if (refFm->getArpeggioEnabled(t)) { - cloneFm->setArpeggioEnabled(t, true); - arpNums.emplace(refFm->getArpeggioNumber(t), -1); - } - } - for (auto& pair : arpNums) pair.second = cloneFMArpeggio(pair.first); - for (auto t : FM_OP_TYPES_) { - if (refFm->getArpeggioEnabled(t)) { - int arpNum = arpNums.at(refFm->getArpeggioNumber(t)); - cloneFm->setArpeggioNumber(t, arpNum); - arpFM_[static_cast(arpNum)]->registerUserInstrument(cloneInstNum); + std::unordered_map arpCloneMap; + for (auto opType : FM_OP_TYPES) { + if (refFm->getArpeggioEnabled(opType)) { + cloneFm->setArpeggioEnabled(opType, true); + int srcNum = refFm->getArpeggioNumber(opType); + if (!arpCloneMap.count(srcNum)) { + arpCloneMap[srcNum] = cloneProperty(arpFM_, srcNum); + } + cloneFm->setArpeggioNumber(opType, arpCloneMap[srcNum]); + arpFM_[static_cast(arpCloneMap[srcNum])]->registerUserInstrument(cloneInstNum); } } - std::unordered_map ptNums; - for (auto t : FM_OP_TYPES_) { - if (refFm->getPitchEnabled(t)) { - cloneFm->setPitchEnabled(t, true); - ptNums.emplace(refFm->getPitchNumber(t), -1); - } - } - for (auto& pair : ptNums) pair.second = cloneFMPitch(pair.first); - for (auto t : FM_OP_TYPES_) { - if (refFm->getPitchEnabled(t)) { - int ptNum = ptNums.at(refFm->getPitchNumber(t)); - cloneFm->setPitchNumber(t, ptNum); - ptFM_[static_cast(ptNum)]->registerUserInstrument(cloneInstNum); + std::unordered_map ptCloneMap; + for (auto opType : FM_OP_TYPES) { + if (refFm->getPitchEnabled(opType)) { + cloneFm->setPitchEnabled(opType, true); + int srcNum = refFm->getPitchNumber(opType); + if (!ptCloneMap.count(srcNum)) { + ptCloneMap[srcNum] = cloneProperty(ptFM_, srcNum); + } + cloneFm->setPitchNumber(opType, ptCloneMap[srcNum]); + ptFM_[static_cast(ptCloneMap[srcNum])]->registerUserInstrument(cloneInstNum); } } - for (auto t : FM_OP_TYPES_) + for (auto t : FM_OP_TYPES) setInstrumentFMEnvelopeResetEnabled(cloneInstNum, t, refFm->getEnvelopeResetEnabled(t)); break; } @@ -387,31 +546,31 @@ if (refSsg->getWaveformEnabled()) { cloneSsg->setWaveformEnabled(true); - int wfNum = cloneSSGWaveform(refSsg->getWaveformNumber()); + int wfNum = cloneProperty(wfSSG_, refSsg->getWaveformNumber()); cloneSsg->setWaveformNumber(wfNum); wfSSG_[static_cast(wfNum)]->registerUserInstrument(cloneInstNum); } if (refSsg->getToneNoiseEnabled()) { cloneSsg->setToneNoiseEnabled(true); - int tnNum = cloneSSGToneNoise(refSsg->getToneNoiseNumber()); + int tnNum = cloneProperty(tnSSG_, refSsg->getToneNoiseNumber()); cloneSsg->setToneNoiseNumber(tnNum); tnSSG_[static_cast(tnNum)]->registerUserInstrument(cloneInstNum); } if (refSsg->getEnvelopeEnabled()) { cloneSsg->setEnvelopeEnabled(true); - int envNum = cloneSSGEnvelope(refSsg->getEnvelopeNumber()); + int envNum = cloneProperty(envSSG_, refSsg->getEnvelopeNumber()); cloneSsg->setEnvelopeNumber(envNum); envSSG_[static_cast(envNum)]->registerUserInstrument(cloneInstNum); } if (refSsg->getArpeggioEnabled()) { cloneSsg->setArpeggioEnabled(true); - int arpNum = cloneSSGArpeggio(refSsg->getArpeggioNumber()); + int arpNum = cloneProperty(arpSSG_, refSsg->getArpeggioNumber()); cloneSsg->setArpeggioNumber(arpNum); arpSSG_[static_cast(arpNum)]->registerUserInstrument(cloneInstNum); } if (refSsg->getPitchEnabled()) { cloneSsg->setPitchEnabled(true); - int ptNum = cloneSSGPitch(refSsg->getPitchNumber()); + int ptNum = cloneProperty(ptSSG_, refSsg->getPitchNumber()); cloneSsg->setPitchNumber(ptNum); ptSSG_[static_cast(ptNum)]->registerUserInstrument(cloneInstNum); } @@ -423,24 +582,24 @@ auto cloneAdpcm = std::dynamic_pointer_cast(insts_.at(static_cast(cloneInstNum))); sampADPCM_[static_cast(cloneAdpcm->getSampleNumber())]->deregisterUserInstrument(cloneInstNum); // Remove temporary number - int sampNum = cloneADPCMSample(refAdpcm->getSampleNumber()); + int sampNum = cloneProperty(sampADPCM_, refAdpcm->getSampleNumber()); cloneAdpcm->setSampleNumber(sampNum); sampADPCM_[static_cast(sampNum)]->registerUserInstrument(cloneInstNum); if (refAdpcm->getEnvelopeEnabled()) { cloneAdpcm->setEnvelopeEnabled(true); - int envNum = cloneADPCMEnvelope(refAdpcm->getEnvelopeNumber()); + int envNum = cloneProperty(envADPCM_, refAdpcm->getEnvelopeNumber()); cloneAdpcm->setEnvelopeNumber(envNum); envADPCM_[static_cast(envNum)]->registerUserInstrument(cloneInstNum); } if (refAdpcm->getArpeggioEnabled()) { cloneAdpcm->setArpeggioEnabled(true); - int arpNum = cloneADPCMArpeggio(refAdpcm->getArpeggioNumber()); + int arpNum = cloneProperty(arpADPCM_, refAdpcm->getArpeggioNumber()); cloneAdpcm->setArpeggioNumber(arpNum); arpADPCM_[static_cast(arpNum)]->registerUserInstrument(cloneInstNum); } if (refAdpcm->getPitchEnabled()) { cloneAdpcm->setPitchEnabled(true); - int ptNum = cloneADPCMPitch(refAdpcm->getPitchNumber()); + int ptNum = cloneProperty(ptADPCM_, refAdpcm->getPitchNumber()); cloneAdpcm->setPitchNumber(ptNum); ptADPCM_[static_cast(ptNum)]->registerUserInstrument(cloneInstNum); } @@ -450,12 +609,12 @@ { auto refKit = std::dynamic_pointer_cast(refInst); auto cloneKit = std::dynamic_pointer_cast(insts_.at(static_cast(cloneInstNum))); - std::unordered_map sampMap; + std::unordered_map sampCloneMap; for (const int& key : refKit->getAssignedKeys()) { - int n = refKit->getSampleNumber(key); - if (!sampMap.count(n)) sampMap[n] = cloneADPCMSample(n); - cloneKit->setSampleNumber(key, sampMap[n]); - sampADPCM_[static_cast(sampMap[n])]->registerUserInstrument(cloneInstNum); + int srcNum = refKit->getSampleNumber(key); + if (!sampCloneMap.count(srcNum)) sampCloneMap[srcNum] = cloneProperty(sampADPCM_, srcNum); + cloneKit->setSampleNumber(key, sampCloneMap[srcNum]); + sampADPCM_[static_cast(sampCloneMap[srcNum])]->registerUserInstrument(cloneInstNum); setInstrumentDrumkitPitch(cloneInstNum, key, refKit->getPitch(key)); } @@ -466,329 +625,59 @@ } } -int InstrumentsManager::cloneFMEnvelope(int srcNum) -{ - int cloneNum = 0; - for (auto& env : envFM_) { - if (!env->isUserInstrument()) { - env = envFM_.at(static_cast(srcNum))->clone(); - env->setNumber(cloneNum); - break; - } - ++cloneNum; - } - return cloneNum; -} - -int InstrumentsManager::cloneFMLFO(int srcNum) -{ - int cloneNum = 0; - for (auto& lfo : lfoFM_) { - if (!lfo->isUserInstrument()) { - lfo = lfoFM_.at(static_cast(srcNum))->clone(); - lfo->setNumber(cloneNum); - break; - } - ++cloneNum; - } - return cloneNum; -} - -int InstrumentsManager::cloneFMOperatorSequence(FMEnvelopeParameter param, int srcNum) -{ - int cloneNum = 0; - for (auto& opSeq : opSeqFM_.at(param)) { - if (!opSeq->isUserInstrument()) { - opSeq = opSeqFM_.at(param).at(static_cast(srcNum))->clone(); - opSeq->setNumber(cloneNum); - break; - } - ++cloneNum; - } - return cloneNum; -} - -int InstrumentsManager::cloneFMArpeggio(int srcNum) -{ - int cloneNum = 0; - for (auto& arp : arpFM_) { - if (!arp->isUserInstrument()) { - arp = arpFM_.at(static_cast(srcNum))->clone(); - arp->setNumber(cloneNum); - break; - } - ++cloneNum; - } - return cloneNum; -} - -int InstrumentsManager::cloneFMPitch(int srcNum) -{ - int cloneNum = 0; - for (auto& pt : ptFM_) { - if (!pt->isUserInstrument()) { - pt = ptFM_.at(static_cast(srcNum))->clone(); - pt->setNumber(cloneNum); - break; - } - ++cloneNum; - } - return cloneNum; -} - -int InstrumentsManager::cloneSSGWaveform(int srcNum) -{ - int cloneNum = 0; - for (auto& wf : wfSSG_) { - if (!wf->isUserInstrument()) { - wf = wfSSG_.at(static_cast(srcNum))->clone(); - wf->setNumber(cloneNum); - break; - } - ++cloneNum; - } - return cloneNum; -} - -int InstrumentsManager::cloneSSGToneNoise(int srcNum) -{ - int cloneNum = 0; - for (auto& tn : tnSSG_) { - if (!tn->isUserInstrument()) { - tn = tnSSG_.at(static_cast(srcNum))->clone(); - tn->setNumber(cloneNum); - break; - } - ++cloneNum; - } - return cloneNum; -} - -int InstrumentsManager::cloneSSGEnvelope(int srcNum) -{ - int cloneNum = 0; - for (auto& env : envSSG_) { - if (!env->isUserInstrument()) { - env = envSSG_.at(static_cast(srcNum))->clone(); - env->setNumber(cloneNum); - break; - } - ++cloneNum; - } - return cloneNum; -} - -int InstrumentsManager::cloneSSGArpeggio(int srcNum) -{ - int cloneNum = 0; - for (auto& arp : arpSSG_) { - if (!arp->isUserInstrument()) { - arp = arpSSG_.at(static_cast(srcNum))->clone(); - arp->setNumber(cloneNum); - break; - } - ++cloneNum; - } - return cloneNum; -} - -int InstrumentsManager::cloneSSGPitch(int srcNum) -{ - int cloneNum = 0; - for (auto& pt : ptSSG_) { - if (!pt->isUserInstrument()) { - pt = ptSSG_.at(static_cast(srcNum))->clone(); - pt->setNumber(cloneNum); - break; - } - ++cloneNum; - } - return cloneNum; -} - -int InstrumentsManager::cloneADPCMSample(int srcNum) -{ - int cloneNum = 0; - for (auto& samp : sampADPCM_) { - if (!samp->isUserInstrument()) { - samp = sampADPCM_.at(static_cast(srcNum))->clone(); - samp->setNumber(cloneNum); - break; - } - ++cloneNum; - } - return cloneNum; -} - -int InstrumentsManager::cloneADPCMEnvelope(int srcNum) -{ - int cloneNum = 0; - for (auto& env : envADPCM_) { - if (!env->isUserInstrument()) { - env = envADPCM_.at(static_cast(srcNum))->clone(); - env->setNumber(cloneNum); - break; - } - ++cloneNum; - } - return cloneNum; -} - -int InstrumentsManager::cloneADPCMArpeggio(int srcNum) -{ - int cloneNum = 0; - for (auto& arp : arpADPCM_) { - if (!arp->isUserInstrument()) { - arp = arpADPCM_.at(static_cast(srcNum))->clone(); - arp->setNumber(cloneNum); - break; - } - ++cloneNum; - } - return cloneNum; -} - -int InstrumentsManager::cloneADPCMPitch(int srcNum) -{ - int cloneNum = 0; - for (auto& pt : ptADPCM_) { - if (!pt->isUserInstrument()) { - pt = ptADPCM_.at(static_cast(srcNum))->clone(); - pt->setNumber(cloneNum); - break; - } - ++cloneNum; - } - return cloneNum; -} - void InstrumentsManager::swapInstruments(int inst1Num, int inst2Num) { std::unique_ptr inst1 = removeInstrument(inst1Num); std::unique_ptr inst2 = removeInstrument(inst2Num); inst1->setNumber(inst2Num); inst2->setNumber(inst1Num); - addInstrument(std::move(inst1)); - addInstrument(std::move(inst2)); -} - -std::unique_ptr InstrumentsManager::removeInstrument(int instNum) -{ - switch (insts_.at(static_cast(instNum))->getType()) { - case InstrumentType::FM: - { - auto fm = std::dynamic_pointer_cast(insts_[static_cast(instNum)]); - envFM_.at(static_cast(fm->getEnvelopeNumber()))->deregisterUserInstrument(instNum); - if (fm->getLFOEnabled()) - lfoFM_.at(static_cast(fm->getLFONumber()))->deregisterUserInstrument(instNum); - for (auto p : ENV_FM_PARAMS_) { - if (fm->getOperatorSequenceEnabled(p)) - opSeqFM_.at(p).at(static_cast(fm->getOperatorSequenceNumber(p))) - ->deregisterUserInstrument(instNum); - } - for (auto t : FM_OP_TYPES_) { - if (fm->getArpeggioEnabled(t)) - arpFM_.at(static_cast(fm->getArpeggioNumber(t)))->deregisterUserInstrument(instNum); - if (fm->getPitchEnabled(t)) - ptFM_.at(static_cast(fm->getPitchNumber(t)))->deregisterUserInstrument(instNum); - } - break; - } - case InstrumentType::SSG: - { - auto ssg = std::dynamic_pointer_cast(insts_[static_cast(instNum)]); - if (ssg->getWaveformEnabled()) - wfSSG_.at(static_cast(ssg->getWaveformNumber()))->deregisterUserInstrument(instNum); - if (ssg->getToneNoiseEnabled()) - tnSSG_.at(static_cast(ssg->getToneNoiseNumber()))->deregisterUserInstrument(instNum); - if (ssg->getEnvelopeEnabled()) - envSSG_.at(static_cast(ssg->getEnvelopeNumber()))->deregisterUserInstrument(instNum); - if (ssg->getArpeggioEnabled()) - arpSSG_.at(static_cast(ssg->getArpeggioNumber()))->deregisterUserInstrument(instNum); - if (ssg->getPitchEnabled()) - ptSSG_.at(static_cast(ssg->getPitchNumber()))->deregisterUserInstrument(instNum); - break; - } - case InstrumentType::ADPCM: - { - auto adpcm = std::dynamic_pointer_cast(insts_[static_cast(instNum)]); - sampADPCM_.at(static_cast(adpcm->getSampleNumber()))->deregisterUserInstrument(instNum); - if (adpcm->getEnvelopeEnabled()) - envADPCM_.at(static_cast(adpcm->getEnvelopeNumber()))->deregisterUserInstrument(instNum); - if (adpcm->getArpeggioEnabled()) - arpADPCM_.at(static_cast(adpcm->getArpeggioNumber()))->deregisterUserInstrument(instNum); - if (adpcm->getPitchEnabled()) - ptADPCM_.at(static_cast(adpcm->getPitchNumber()))->deregisterUserInstrument(instNum); - break; - } - case InstrumentType::Drumkit: - { - auto kit = std::dynamic_pointer_cast(insts_[static_cast(instNum)]); - for (const int& key : kit->getAssignedKeys()) { - sampADPCM_.at(static_cast(kit->getSampleNumber(key)))->deregisterUserInstrument(instNum); - } - break; - } - default: - throw std::invalid_argument("invalid instrument type"); - } - - std::unique_ptr clone(insts_[static_cast(instNum)]->clone()); - insts_[static_cast(instNum)].reset(); - return clone; + addInstrument(inst1.release()); + addInstrument(inst2.release()); } +/// [Return] instrument shared pointer or nullptr std::shared_ptr InstrumentsManager::getInstrumentSharedPtr(int instNum) { - if (0 <= instNum && instNum < static_cast(insts_.size()) - && insts_.at(static_cast(instNum)) != nullptr) { + if (0 <= instNum && instNum < static_cast(insts_.size())) return insts_[static_cast(instNum)]; - } - else { - return std::shared_ptr(); // Return nullptr - } + else + return std::shared_ptr(); } void InstrumentsManager::clearAll() { - for (auto p : ENV_FM_PARAMS_) { - opSeqFM_.emplace(p, std::array, 128>()); + for (auto p : ENV_FM_PARAMS) { + opSeqFM_.emplace(p, std::array>, 128>()); } for (size_t i = 0; i < 128; ++i) { insts_[i].reset(); - envFM_[i] = std::make_shared(i); - lfoFM_[i] = std::make_shared(i); - for (auto& p : opSeqFM_) { - p.second[i] = std::make_shared(i); - } - arpFM_[i] = std::make_shared(i, SequenceType::ABSOLUTE_SEQUENCE, 48); - ptFM_[i] = std::make_shared(i, SequenceType::ABSOLUTE_SEQUENCE, 127); - - wfSSG_[i] = std::make_shared(i); - tnSSG_[i] = std::make_shared(i); - envSSG_[i] = std::make_shared(i, SequenceType::NO_SEQUENCE_TYPE, 15); - arpSSG_[i] = std::make_shared(i, SequenceType::ABSOLUTE_SEQUENCE, 48); - ptSSG_[i] = std::make_shared(i, SequenceType::ABSOLUTE_SEQUENCE, 127); - - sampADPCM_[i] = std::make_shared(i); - envADPCM_[i] = std::make_shared(i, SequenceType::NO_SEQUENCE_TYPE, 255); - arpADPCM_[i] = std::make_shared(i, SequenceType::ABSOLUTE_SEQUENCE, 48); - ptADPCM_[i] = std::make_shared(i, SequenceType::ABSOLUTE_SEQUENCE, 127); + envFM_[i] = makeEnvelopeFMSharedPtr(i); + lfoFM_[i] = makeLFOFMSharedPtr(i); + for (auto& p : opSeqFM_) p.second[i] = makeOperatorSequenceFMSharedPtr(i); + arpFM_[i] = makeArpeggioSharedPtr(i); + ptFM_[i] = makePitchSharedPtr(i); + + wfSSG_[i] = makeWaveformSSGSharedPtr(i); + tnSSG_[i] = makeToneNoiseSSGSharedPtr(i); + envSSG_[i] = makeEnvelopeSSGSharedPtr(i); + arpSSG_[i] = makeArpeggioSharedPtr(i); + ptSSG_[i] = makePitchSharedPtr(i); + + sampADPCM_[i] = makeSampleADPCMSharedPtr(i); + envADPCM_[i] = makeEnvelopeADPCMSharedPtr(i); + arpADPCM_[i] = makeArpeggioSharedPtr(i); + ptADPCM_[i] = makePitchSharedPtr(i); } } std::vector InstrumentsManager::getInstrumentIndices() const { - std::vector idcs; - for (size_t i = 0; i < insts_.size(); ++i) { - if (insts_[i]) idcs.push_back(static_cast(i)); - } - return idcs; + return utils::findIndicesIf(insts_, [](decltype(insts_)::const_reference inst) { return inst; }); } -void InstrumentsManager::setInstrumentName(int instNum, std::string name) +void InstrumentsManager::setInstrumentName(int instNum, const std::string& name) { insts_.at(static_cast(instNum))->setName(name); } @@ -800,77 +689,64 @@ std::vector InstrumentsManager::getInstrumentNameList() const { - std::vector names; - for (auto& inst : insts_) { - if (inst) names.push_back(inst->getName()); - } - return names; -} - -std::vector InstrumentsManager::getEntriedInstrumentIndices() const -{ - std::vector idcs; - int n = 0; - for (auto& inst : insts_) { - if (inst) idcs.push_back(n); - ++n; - } - return idcs; + using InstRef = decltype(insts_)::const_reference; + return utils::transformIf(insts_, [](InstRef inst) { return inst; }, + [](InstRef inst) { return inst->getName(); }); } void InstrumentsManager::clearUnusedInstrumentProperties() { for (size_t i = 0; i < 128; ++i) { if (!envFM_[i]->isUserInstrument()) - envFM_[i] = std::make_shared(i); + envFM_[i] = makeEnvelopeFMSharedPtr(i); if (!lfoFM_[i]->isUserInstrument()) - lfoFM_[i] = std::make_shared(i); + lfoFM_[i] = makeLFOFMSharedPtr(i); for (auto& p : opSeqFM_) { if (!p.second[i]->isUserInstrument()) - p.second[i] = std::make_shared(i); + p.second[i] = makeOperatorSequenceFMSharedPtr(i); } if (!arpFM_[i]->isUserInstrument()) - arpFM_[i] = std::make_shared(i, SequenceType::ABSOLUTE_SEQUENCE, 48); + arpFM_[i] = makeArpeggioSharedPtr(i); if (!ptFM_[i]->isUserInstrument()) - ptFM_[i] = std::make_shared(i, SequenceType::ABSOLUTE_SEQUENCE, 127); + ptFM_[i] = makePitchSharedPtr(i); if (!wfSSG_[i]->isUserInstrument()) - wfSSG_[i] = std::make_shared(i); + wfSSG_[i] = makeWaveformSSGSharedPtr(i); if (!tnSSG_[i]->isUserInstrument()) - tnSSG_[i] = std::make_shared(i); + tnSSG_[i] = makeToneNoiseSSGSharedPtr(i); if (!envSSG_[i]->isUserInstrument()) - envSSG_[i] = std::make_shared(i, SequenceType::NO_SEQUENCE_TYPE, 15); + envSSG_[i] = makeEnvelopeSSGSharedPtr(i); if (!arpSSG_[i]->isUserInstrument()) - arpSSG_[i] = std::make_shared(i, SequenceType::ABSOLUTE_SEQUENCE, 48); + arpSSG_[i] = makeArpeggioSharedPtr(i); if (!ptSSG_[i]->isUserInstrument()) - ptSSG_[i] = std::make_shared(i, SequenceType::ABSOLUTE_SEQUENCE, 127); + ptSSG_[i] = makePitchSharedPtr(i); if (!sampADPCM_[i]->isUserInstrument()) - sampADPCM_[i] = std::make_shared(i); + sampADPCM_[i] = makeSampleADPCMSharedPtr(i); if (!envADPCM_[i]->isUserInstrument()) - envADPCM_[i] = std::make_shared(i, SequenceType::NO_SEQUENCE_TYPE, 255); + envADPCM_[i] = makeEnvelopeADPCMSharedPtr(i); if (!arpADPCM_[i]->isUserInstrument()) - arpADPCM_[i] = std::make_shared(i, SequenceType::ABSOLUTE_SEQUENCE, 48); + arpADPCM_[i] = makeArpeggioSharedPtr(i); if (!ptADPCM_[i]->isUserInstrument()) - ptADPCM_[i] = std::make_shared(i, SequenceType::ABSOLUTE_SEQUENCE, 127); + ptADPCM_[i] = makePitchSharedPtr(i); } } -/// Return: +/// [Return] /// -1: no free instrument /// else: first free instrument number int InstrumentsManager::findFirstFreeInstrument() const { - auto&& it = std::find_if_not(insts_.begin(), insts_.end(), + auto&& it = std::find_if_not(insts_.cbegin(), insts_.cend(), [](const std::shared_ptr& inst) { return inst; }); - return (it == insts_.end() ? -1 : std::distance(insts_.begin(), it)); + return (it == insts_.cend() ? -1 : std::distance(insts_.cbegin(), it)); } -std::vector> InstrumentsManager::checkDuplicateInstruments() const +std::unordered_map InstrumentsManager::getDuplicateInstrumentMap() const { - std::vector> dupList; - std::vector idcs = getEntriedInstrumentIndices(); - std::unordered_map dupMap; + std::vector idcs = getInstrumentIndices(); + static const std::unordered_map, std::shared_ptr) const> eqCheck = { { InstrumentType::FM, &InstrumentsManager::equalPropertiesFM }, @@ -881,29 +757,21 @@ for (size_t i = 0; i < idcs.size(); ++i) { int baseIdx = idcs[i]; - std::vector group { baseIdx }; std::shared_ptr base = insts_[baseIdx]; for (size_t j = i + 1; j < idcs.size();) { int tgtIdx = idcs[j]; std::shared_ptr tgt = insts_[tgtIdx]; if (base->getType() == tgt->getType() && (this->*eqCheck.at(base->getType()))(base, tgt)) { - group.push_back(tgtIdx); + dupMap[tgtIdx] = baseIdx; idcs.erase(idcs.begin() + j); continue; } ++j; } - - if (group.size() > 1) dupList.push_back(group); } - return dupList; -} - -void InstrumentsManager::setPropertyFindMode(bool unedited) -{ - regardingUnedited_ = unedited; + return dupMap; } //----- FM methods ----- @@ -918,7 +786,7 @@ int InstrumentsManager::getInstrumentFMEnvelope(int instNum) const { - return std::dynamic_pointer_cast(insts_[static_cast(instNum)])->getEnvelopeNumber(); + return std::dynamic_pointer_cast(insts_.at(static_cast(instNum)))->getEnvelopeNumber(); } void InstrumentsManager::setEnvelopeFMParameter(int envNum, FMEnvelopeParameter param, int value) @@ -941,35 +809,19 @@ return envFM_.at(static_cast(envNum))->getOperatorEnabled(opNum); } -std::vector InstrumentsManager::getEnvelopeFMUsers(int envNum) const +std::multiset InstrumentsManager::getEnvelopeFMUsers(int envNum) const { return envFM_.at(static_cast(envNum))->getUserInstruments(); } std::vector InstrumentsManager::getEnvelopeFMEntriedIndices() const { - std::vector idcs; - int n = 0; - for (auto& env : envFM_) { - if (env->isEdited()) idcs.push_back(n); - ++n; - } - return idcs; + return utils::findIndicesIf(envFM_, IsEdited()); } int InstrumentsManager::findFirstAssignableEnvelopeFM() const { - std::function&)> cond; - if (regardingUnedited_) - cond = [](const std::shared_ptr& env) { return (env->isUserInstrument() || env->isEdited()); }; - else - cond = [](const std::shared_ptr& env) { return env->isUserInstrument(); }; - auto&& it = std::find_if_not(envFM_.begin(), envFM_.end(), cond); - - if (it == envFM_.end()) return -1; - - if (!regardingUnedited_) (*it)->clearParameters(); - return std::distance(envFM_.begin(), it); + return findFirstAssignableProperty(envFM_, regardingUnedited_); } void InstrumentsManager::setInstrumentFMLFOEnabled(int instNum, bool enabled) @@ -996,7 +848,7 @@ int InstrumentsManager::getInstrumentFMLFO(int instNum) const { - return std::dynamic_pointer_cast(insts_[static_cast(instNum)])->getLFONumber(); + return std::dynamic_pointer_cast(insts_.at(static_cast(instNum)))->getLFONumber(); } void InstrumentsManager::setLFOFMParameter(int lfoNum, FMLFOParameter param, int value) @@ -1009,35 +861,19 @@ return lfoFM_.at(static_cast(lfoNum))->getParameterValue(param); } -std::vector InstrumentsManager::getLFOFMUsers(int lfoNum) const +std::multiset InstrumentsManager::getLFOFMUsers(int lfoNum) const { return lfoFM_.at(static_cast(lfoNum))->getUserInstruments(); } std::vector InstrumentsManager::getLFOFMEntriedIndices() const { - std::vector idcs; - int n = 0; - for (auto& lfo : lfoFM_) { - if (lfo->isEdited()) idcs.push_back(n); - ++n; - } - return idcs; + return utils::findIndicesIf(lfoFM_, IsEdited()); } int InstrumentsManager::findFirstAssignableLFOFM() const { - std::function&)> cond; - if (regardingUnedited_) - cond = [](const std::shared_ptr& lfo) { return (lfo->isUserInstrument() || lfo->isEdited()); }; - else - cond = [](const std::shared_ptr& lfo) { return lfo->isUserInstrument(); }; - auto&& it = std::find_if_not(lfoFM_.begin(), lfoFM_.end(), cond); - - if (it == lfoFM_.end()) return -1; - - if (!regardingUnedited_) (*it)->clearParameters(); - return std::distance(lfoFM_.begin(), it); + return findFirstAssignableProperty(lfoFM_, regardingUnedited_); } void InstrumentsManager::setInstrumentFMOperatorSequenceEnabled(int instNum, FMEnvelopeParameter param, bool enabled) @@ -1067,84 +903,82 @@ int InstrumentsManager::getInstrumentFMOperatorSequence(int instNum, FMEnvelopeParameter param) { - return std::dynamic_pointer_cast(insts_[static_cast(instNum)])->getOperatorSequenceNumber(param); + return std::dynamic_pointer_cast(insts_.at(static_cast(instNum)))->getOperatorSequenceNumber(param); } -void InstrumentsManager::addOperatorSequenceFMSequenceCommand(FMEnvelopeParameter param, int opSeqNum, int type, int data) +void InstrumentsManager::addOperatorSequenceFMSequenceData(FMEnvelopeParameter param, int opSeqNum, int data) { - opSeqFM_.at(param).at(static_cast(opSeqNum))->addSequenceCommand(type, data); + opSeqFM_.at(param).at(static_cast(opSeqNum))->addSequenceUnit(FMOperatorSequenceUnit(data)); } -void InstrumentsManager::removeOperatorSequenceFMSequenceCommand(FMEnvelopeParameter param, int opSeqNum) +void InstrumentsManager::removeOperatorSequenceFMSequenceData(FMEnvelopeParameter param, int opSeqNum) { - opSeqFM_.at(param).at(static_cast(opSeqNum))->removeSequenceCommand(); + opSeqFM_.at(param).at(static_cast(opSeqNum))->removeSequenceUnit(); } -void InstrumentsManager::setOperatorSequenceFMSequenceCommand(FMEnvelopeParameter param, int opSeqNum, int cnt, int type, int data) +void InstrumentsManager::setOperatorSequenceFMSequenceData(FMEnvelopeParameter param, int opSeqNum, int cnt, int data) { - opSeqFM_.at(param).at(static_cast(opSeqNum))->setSequenceCommand(cnt, type, data); + opSeqFM_.at(param).at(static_cast(opSeqNum))->setSequenceUnit(cnt, FMOperatorSequenceUnit(data)); } -std::vector InstrumentsManager::getOperatorSequenceFMSequence(FMEnvelopeParameter param, int opSeqNum) +std::vector InstrumentsManager::getOperatorSequenceFMSequence(FMEnvelopeParameter param, int opSeqNum) { return opSeqFM_.at(param).at(static_cast(opSeqNum))->getSequence(); } -void InstrumentsManager::setOperatorSequenceFMLoops(FMEnvelopeParameter param, int opSeqNum, std::vector begins, std::vector ends, std::vector times) +void InstrumentsManager::addOperatorSequenceFMLoop(FMEnvelopeParameter param, int opSeqNum, const InstrumentSequenceLoop& loop) { - opSeqFM_.at(param).at(static_cast(opSeqNum))->setLoops(std::move(begins), std::move(ends), std::move(times)); + opSeqFM_.at(param).at(static_cast(opSeqNum))->addLoop(loop); } -std::vector InstrumentsManager::getOperatorSequenceFMLoops(FMEnvelopeParameter param, int opSeqNum) const +void InstrumentsManager::removeOperatorSequenceFMLoop(FMEnvelopeParameter param, int opSeqNum, int begin, int end) { - return opSeqFM_.at(param).at(static_cast(opSeqNum))->getLoops(); + opSeqFM_.at(param).at(static_cast(opSeqNum))->removeLoop(begin, end); } -void InstrumentsManager::setOperatorSequenceFMRelease(FMEnvelopeParameter param, int opSeqNum, ReleaseType type, int begin) +void InstrumentsManager::changeOperatorSequenceFMLoop(FMEnvelopeParameter param, int opSeqNum, int prevBegin, int prevEnd, const InstrumentSequenceLoop& loop) { - opSeqFM_.at(param).at(static_cast(opSeqNum))->setRelease(type, begin); + opSeqFM_.at(param).at(static_cast(opSeqNum))->changeLoop(prevBegin, prevEnd, loop); } -Release InstrumentsManager::getOperatorSequenceFMRelease(FMEnvelopeParameter param, int opSeqNum) const +void InstrumentsManager::clearOperatorSequenceFMLoops(FMEnvelopeParameter param, int opSeqNum) +{ + opSeqFM_.at(param).at(static_cast(opSeqNum))->clearLoops(); +} + +InstrumentSequenceLoopRoot InstrumentsManager::getOperatorSequenceFMLoopRoot(FMEnvelopeParameter param, int opSeqNum) const +{ + return opSeqFM_.at(param).at(static_cast(opSeqNum))->getLoopRoot(); +} + +void InstrumentsManager::setOperatorSequenceFMRelease(FMEnvelopeParameter param, int opSeqNum, const InstrumentSequenceRelease& release) +{ + opSeqFM_.at(param).at(static_cast(opSeqNum))->setRelease(release); +} + +InstrumentSequenceRelease InstrumentsManager::getOperatorSequenceFMRelease(FMEnvelopeParameter param, int opSeqNum) const { return opSeqFM_.at(param).at(static_cast(opSeqNum))->getRelease(); } -std::unique_ptr InstrumentsManager::getOperatorSequenceFMIterator(FMEnvelopeParameter param, int opSeqNum) const +FMOperatorSequenceIter InstrumentsManager::getOperatorSequenceFMIterator(FMEnvelopeParameter param, int opSeqNum) const { return opSeqFM_.at(param).at(static_cast(opSeqNum))->getIterator(); } -std::vector InstrumentsManager::getOperatorSequenceFMUsers(FMEnvelopeParameter param, int opSeqNum) const +std::multiset InstrumentsManager::getOperatorSequenceFMUsers(FMEnvelopeParameter param, int opSeqNum) const { return opSeqFM_.at(param).at(static_cast(opSeqNum))->getUserInstruments(); } std::vector InstrumentsManager::getOperatorSequenceFMEntriedIndices(FMEnvelopeParameter param) const { - std::vector idcs; - int n = 0; - for (auto& seq : opSeqFM_.at(param)) { - if (seq->isUserInstrument() || seq->isEdited()) idcs.push_back(n); - ++n; - } - return idcs; + return utils::findIndicesIf(opSeqFM_.at(param), IsUsedOrEdited()); } int InstrumentsManager::findFirstAssignableOperatorSequenceFM(FMEnvelopeParameter param) const { - std::function&)> cond; - if (regardingUnedited_) - cond = [](const std::shared_ptr& seq) { return (seq->isUserInstrument() || seq->isEdited()); }; - else - cond = [](const std::shared_ptr& seq) { return seq->isUserInstrument(); }; - auto& opSeq = opSeqFM_.at(param); - auto&& it = std::find_if_not(opSeq.begin(), opSeq.end(), cond); - - if (it == opSeq.end()) return -1; - - if (!regardingUnedited_) (*it)->clearParameters(); - return std::distance(opSeq.begin(), it); + return findFirstAssignableProperty(opSeqFM_.at(param), regardingUnedited_); } void InstrumentsManager::setInstrumentFMArpeggioEnabled(int instNum, FMOperatorType op, bool enabled) @@ -1174,7 +1008,7 @@ int InstrumentsManager::getInstrumentFMArpeggio(int instNum, FMOperatorType op) { - return std::dynamic_pointer_cast(insts_[static_cast(instNum)])->getArpeggioNumber(op); + return std::dynamic_pointer_cast(insts_.at(static_cast(instNum)))->getArpeggioNumber(op); } void InstrumentsManager::setArpeggioFMType(int arpNum, SequenceType type) @@ -1187,80 +1021,79 @@ return arpFM_.at(static_cast(arpNum))->getType(); } -void InstrumentsManager::addArpeggioFMSequenceCommand(int arpNum, int type, int data) +void InstrumentsManager::addArpeggioFMSequenceData(int arpNum, int data) { - arpFM_.at(static_cast(arpNum))->addSequenceCommand(type, data); + arpFM_.at(static_cast(arpNum))->addSequenceUnit(ArpeggioUnit(data)); } -void InstrumentsManager::removeArpeggioFMSequenceCommand(int arpNum) +void InstrumentsManager::removeArpeggioFMSequenceData(int arpNum) { - arpFM_.at(static_cast(arpNum))->removeSequenceCommand(); + arpFM_.at(static_cast(arpNum))->removeSequenceUnit(); } -void InstrumentsManager::setArpeggioFMSequenceCommand(int arpNum, int cnt, int type, int data) +void InstrumentsManager::setArpeggioFMSequenceData(int arpNum, int cnt, int data) { - arpFM_.at(static_cast(arpNum))->setSequenceCommand(cnt, type, data); + arpFM_.at(static_cast(arpNum))->setSequenceUnit(cnt, ArpeggioUnit(data)); } -std::vector InstrumentsManager::getArpeggioFMSequence(int arpNum) +std::vector InstrumentsManager::getArpeggioFMSequence(int arpNum) { return arpFM_.at(static_cast(arpNum))->getSequence(); } -void InstrumentsManager::setArpeggioFMLoops(int arpNum, std::vector begins, std::vector ends, std::vector times) +void InstrumentsManager::addArpeggioFMLoop(int arpNum, const InstrumentSequenceLoop& loop) +{ + arpFM_.at(static_cast(arpNum))->addLoop(loop); +} + +void InstrumentsManager::removeArpeggioFMLoop(int arpNum, int begin, int end) +{ + arpFM_.at(static_cast(arpNum))->removeLoop(begin, end); +} + +void InstrumentsManager::changeArpeggioFMLoop(int arpNum, int prevBegin, int prevEnd, const InstrumentSequenceLoop& loop) { - arpFM_.at(static_cast(arpNum))->setLoops(std::move(begins), std::move(ends), std::move(times)); + arpFM_.at(static_cast(arpNum))->changeLoop(prevBegin, prevEnd, loop); } -std::vector InstrumentsManager::getArpeggioFMLoops(int arpNum) const +void InstrumentsManager::clearArpeggioFMLoops(int arpNum) { - return arpFM_.at(static_cast(arpNum))->getLoops(); + arpFM_.at(static_cast(arpNum))->clearLoops(); } -void InstrumentsManager::setArpeggioFMRelease(int arpNum, ReleaseType type, int begin) +InstrumentSequenceLoopRoot InstrumentsManager::getArpeggioFMLoopRoot(int arpNum) const { - arpFM_.at(static_cast(arpNum))->setRelease(type, begin); + return arpFM_.at(static_cast(arpNum))->getLoopRoot(); } -Release InstrumentsManager::getArpeggioFMRelease(int arpNum) const +void InstrumentsManager::setArpeggioFMRelease(int arpNum, const InstrumentSequenceRelease& release) +{ + arpFM_.at(static_cast(arpNum))->setRelease(release); +} + +InstrumentSequenceRelease InstrumentsManager::getArpeggioFMRelease(int arpNum) const { return arpFM_.at(static_cast(arpNum))->getRelease(); } -std::unique_ptr InstrumentsManager::getArpeggioFMIterator(int arpNum) const +ArpeggioIter InstrumentsManager::getArpeggioFMIterator(int arpNum) const { return arpFM_.at(static_cast(arpNum))->getIterator(); } -std::vector InstrumentsManager::getArpeggioFMUsers(int arpNum) const +std::multiset InstrumentsManager::getArpeggioFMUsers(int arpNum) const { return arpFM_.at(static_cast(arpNum))->getUserInstruments(); } std::vector InstrumentsManager::getArpeggioFMEntriedIndices() const { - std::vector idcs; - int n = 0; - for (auto& arp : arpFM_) { - if (arp->isUserInstrument() || arp->isEdited()) idcs.push_back(n); - ++n; - } - return idcs; + return utils::findIndicesIf(arpFM_, IsUsedOrEdited()); } int InstrumentsManager::findFirstAssignableArpeggioFM() const { - std::function&)> cond; - if (regardingUnedited_) - cond = [](const std::shared_ptr& arp) { return (arp->isUserInstrument() || arp->isEdited()); }; - else - cond = [](const std::shared_ptr& arp) { return arp->isUserInstrument(); }; - auto&& it = std::find_if_not(arpFM_.begin(), arpFM_.end(), cond); - - if (it == arpFM_.end()) return -1; - - if (!regardingUnedited_) (*it)->clearParameters(); - return std::distance(arpFM_.begin(), it); + return findFirstAssignableProperty(arpFM_, regardingUnedited_); } void InstrumentsManager::setInstrumentFMPitchEnabled(int instNum, FMOperatorType op, bool enabled) @@ -1290,7 +1123,7 @@ int InstrumentsManager::getInstrumentFMPitch(int instNum, FMOperatorType op) { - return std::dynamic_pointer_cast(insts_[static_cast(instNum)])->getPitchNumber(op); + return std::dynamic_pointer_cast(insts_.at(static_cast(instNum)))->getPitchNumber(op); } void InstrumentsManager::setPitchFMType(int ptNum, SequenceType type) @@ -1303,85 +1136,84 @@ return ptFM_.at(static_cast(ptNum))->getType(); } -void InstrumentsManager::addPitchFMSequenceCommand(int ptNum, int type, int data) +void InstrumentsManager::addPitchFMSequenceData(int ptNum, int data) { - ptFM_.at(static_cast(ptNum))->addSequenceCommand(type, data); + ptFM_.at(static_cast(ptNum))->addSequenceUnit(PitchUnit(data)); } -void InstrumentsManager::removePitchFMSequenceCommand(int ptNum) +void InstrumentsManager::removePitchFMSequenceData(int ptNum) { - ptFM_.at(static_cast(ptNum))->removeSequenceCommand(); + ptFM_.at(static_cast(ptNum))->removeSequenceUnit(); } -void InstrumentsManager::setPitchFMSequenceCommand(int ptNum, int cnt, int type, int data) +void InstrumentsManager::setPitchFMSequenceData(int ptNum, int cnt, int data) { - ptFM_.at(static_cast(ptNum))->setSequenceCommand(cnt, type, data); + ptFM_.at(static_cast(ptNum))->setSequenceUnit(cnt, PitchUnit(data)); } -std::vector InstrumentsManager::getPitchFMSequence(int ptNum) +std::vector InstrumentsManager::getPitchFMSequence(int ptNum) { return ptFM_.at(static_cast(ptNum))->getSequence(); } -void InstrumentsManager::setPitchFMLoops(int ptNum, std::vector begins, std::vector ends, std::vector times) +void InstrumentsManager::addPitchFMLoop(int ptNum, const InstrumentSequenceLoop& loop) { - ptFM_.at(static_cast(ptNum))->setLoops(std::move(begins), std::move(ends), std::move(times)); + ptFM_.at(static_cast(ptNum))->addLoop(loop); } -std::vector InstrumentsManager::getPitchFMLoops(int ptNum) const +void InstrumentsManager::removePitchFMLoop(int ptNum, int begin, int end) { - return ptFM_.at(static_cast(ptNum))->getLoops(); + ptFM_.at(static_cast(ptNum))->removeLoop(begin, end); } -void InstrumentsManager::setPitchFMRelease(int ptNum, ReleaseType type, int begin) +void InstrumentsManager::changePitchFMLoop(int ptNum, int prevBegin, int prevEnd, const InstrumentSequenceLoop& loop) { - ptFM_.at(static_cast(ptNum))->setRelease(type, begin); + ptFM_.at(static_cast(ptNum))->changeLoop(prevBegin, prevEnd, loop); } -Release InstrumentsManager::getPitchFMRelease(int ptNum) const +void InstrumentsManager::clearPitchFMLoops(int ptNum) +{ + ptFM_.at(static_cast(ptNum))->clearLoops(); +} + +InstrumentSequenceLoopRoot InstrumentsManager::getPitchFMLoopRoot(int ptNum) const +{ + return ptFM_.at(static_cast(ptNum))->getLoopRoot(); +} + +void InstrumentsManager::setPitchFMRelease(int ptNum, const InstrumentSequenceRelease& release) +{ + ptFM_.at(static_cast(ptNum))->setRelease(release); +} + +InstrumentSequenceRelease InstrumentsManager::getPitchFMRelease(int ptNum) const { return ptFM_.at(static_cast(ptNum))->getRelease(); } -std::unique_ptr InstrumentsManager::getPitchFMIterator(int ptNum) const +PitchIter InstrumentsManager::getPitchFMIterator(int ptNum) const { return ptFM_.at(static_cast(ptNum))->getIterator(); } -std::vector InstrumentsManager::getPitchFMUsers(int ptNum) const +std::multiset InstrumentsManager::getPitchFMUsers(int ptNum) const { return ptFM_.at(static_cast(ptNum))->getUserInstruments(); } void InstrumentsManager::setInstrumentFMEnvelopeResetEnabled(int instNum, FMOperatorType op, bool enabled) { - std::dynamic_pointer_cast(insts_[static_cast(instNum)])->setEnvelopeResetEnabled(op, enabled); + std::dynamic_pointer_cast(insts_.at(static_cast(instNum)))->setEnvelopeResetEnabled(op, enabled); } std::vector InstrumentsManager::getPitchFMEntriedIndices() const { - std::vector idcs; - int n = 0; - for (auto& pt : ptFM_) { - if (pt->isUserInstrument() || pt->isEdited()) idcs.push_back(n); - ++n; - } - return idcs; + return utils::findIndicesIf(ptFM_, IsUsedOrEdited()); } int InstrumentsManager::findFirstAssignablePitchFM() const { - std::function&)> cond; - if (regardingUnedited_) - cond = [](const std::shared_ptr& pt) { return (pt->isUserInstrument() || pt->isEdited()); }; - else - cond = [](const std::shared_ptr& pt) { return pt->isUserInstrument(); }; - auto&& it = std::find_if_not(ptFM_.begin(), ptFM_.end(), cond); - - if (it == ptFM_.end()) return -1; - - if (!regardingUnedited_) (*it)->clearParameters(); - return std::distance(ptFM_.begin(), it); + return findFirstAssignableProperty(ptFM_, regardingUnedited_); } bool InstrumentsManager::equalPropertiesFM(std::shared_ptr a, std::shared_ptr b) const @@ -1389,29 +1221,29 @@ auto aFm = std::dynamic_pointer_cast(a); auto bFm = std::dynamic_pointer_cast(b); - if (*envFM_[aFm->getEnvelopeNumber()].get() != *envFM_[bFm->getEnvelopeNumber()].get()) + if (*envFM_[aFm->getEnvelopeNumber()] != *envFM_[bFm->getEnvelopeNumber()]) return false; if (aFm->getLFOEnabled() != bFm->getLFOEnabled()) return false; - if (aFm->getLFOEnabled() && *lfoFM_[aFm->getLFONumber()].get() != *lfoFM_[bFm->getLFONumber()].get()) + if (aFm->getLFOEnabled() && *lfoFM_[aFm->getLFONumber()] != *lfoFM_[bFm->getLFONumber()]) return false; for (auto& pair : opSeqFM_) { if (aFm->getOperatorSequenceEnabled(pair.first) != bFm->getOperatorSequenceEnabled(pair.first)) return false; if (aFm->getOperatorSequenceEnabled(pair.first) - && *pair.second[aFm->getOperatorSequenceNumber(pair.first)].get() != *pair.second[bFm->getOperatorSequenceNumber(pair.first)].get()) + && *pair.second[aFm->getOperatorSequenceNumber(pair.first)] != *pair.second[bFm->getOperatorSequenceNumber(pair.first)]) return false; } - for (auto& type : FM_OP_TYPES_) { + for (auto& type : FM_OP_TYPES) { if (aFm->getArpeggioEnabled(type) != bFm->getArpeggioEnabled(type)) return false; if (aFm->getArpeggioEnabled(type) - && *arpFM_[aFm->getArpeggioNumber(type)].get() != *arpFM_[bFm->getArpeggioNumber(type)].get()) + && *arpFM_[aFm->getArpeggioNumber(type)] != *arpFM_[bFm->getArpeggioNumber(type)]) return false; if (aFm->getPitchEnabled(type) != bFm->getPitchEnabled(type)) return false; if (aFm->getPitchEnabled(type) - && *ptFM_[aFm->getPitchNumber(type)].get() != *ptFM_[bFm->getPitchNumber(type)].get()) + && *ptFM_[aFm->getPitchNumber(type)] != *ptFM_[bFm->getPitchNumber(type)]) return false; if (aFm->getEnvelopeResetEnabled(type) != bFm->getEnvelopeResetEnabled(type)) return false; @@ -1447,83 +1279,82 @@ int InstrumentsManager::getInstrumentSSGWaveform(int instNum) { - return std::dynamic_pointer_cast(insts_[static_cast(instNum)])->getWaveformNumber(); + return std::dynamic_pointer_cast(insts_.at(static_cast(instNum)))->getWaveformNumber(); } -void InstrumentsManager::addWaveformSSGSequenceCommand(int wfNum, int type, int data) +void InstrumentsManager::addWaveformSSGSequenceData(int wfNum, const SSGWaveformUnit& data) { - wfSSG_.at(static_cast(wfNum))->addSequenceCommand(type, data); + wfSSG_.at(static_cast(wfNum))->addSequenceUnit(data); } -void InstrumentsManager::removeWaveformSSGSequenceCommand(int wfNum) +void InstrumentsManager::removeWaveformSSGSequenceData(int wfNum) { - wfSSG_.at(static_cast(wfNum))->removeSequenceCommand(); + wfSSG_.at(static_cast(wfNum))->removeSequenceUnit(); } -void InstrumentsManager::setWaveformSSGSequenceCommand(int wfNum, int cnt, int type, int data) +void InstrumentsManager::setWaveformSSGSequenceData(int wfNum, int cnt, const SSGWaveformUnit& data) { - wfSSG_.at(static_cast(wfNum))->setSequenceCommand(cnt, type, data); + wfSSG_.at(static_cast(wfNum))->setSequenceUnit(cnt, data); } -std::vector InstrumentsManager::getWaveformSSGSequence(int wfNum) +std::vector InstrumentsManager::getWaveformSSGSequence(int wfNum) { return wfSSG_.at(static_cast(wfNum))->getSequence(); } -void InstrumentsManager::setWaveformSSGLoops(int wfNum, std::vector begins, std::vector ends, std::vector times) +void InstrumentsManager::addWaveformSSGLoop(int wfNum, const InstrumentSequenceLoop& loop) +{ + wfSSG_.at(static_cast(wfNum))->addLoop(loop); +} + +void InstrumentsManager::removeWaveformSSGLoop(int wfNum, int begin, int end) { - wfSSG_.at(static_cast(wfNum))->setLoops(std::move(begins), std::move(ends), std::move(times)); + wfSSG_.at(static_cast(wfNum))->removeLoop(begin, end); } -std::vector InstrumentsManager::getWaveformSSGLoops(int wfNum) const +void InstrumentsManager::changeWaveformSSGLoop(int wfNum, int prevBegin, int prevEnd, const InstrumentSequenceLoop& loop) { - return wfSSG_.at(static_cast(wfNum))->getLoops(); + wfSSG_.at(static_cast(wfNum))->changeLoop(prevBegin, prevEnd, loop); } -void InstrumentsManager::setWaveformSSGRelease(int wfNum, ReleaseType type, int begin) +void InstrumentsManager::clearWaveformSSGLoops(int wfNum) { - wfSSG_.at(static_cast(wfNum))->setRelease(type, begin); + wfSSG_.at(static_cast(wfNum))->clearLoops(); } -Release InstrumentsManager::getWaveformSSGRelease(int wfNum) const +InstrumentSequenceLoopRoot InstrumentsManager::getWaveformSSGLoopRoot(int wfNum) const +{ + return wfSSG_.at(static_cast(wfNum))->getLoopRoot(); +} + +void InstrumentsManager::setWaveformSSGRelease(int wfNum, const InstrumentSequenceRelease& release) +{ + wfSSG_.at(static_cast(wfNum))->setRelease(release); +} + +InstrumentSequenceRelease InstrumentsManager::getWaveformSSGRelease(int wfNum) const { return wfSSG_.at(static_cast(wfNum))->getRelease(); } -std::unique_ptr InstrumentsManager::getWaveformSSGIterator(int wfNum) const +SSGWaveformIter InstrumentsManager::getWaveformSSGIterator(int wfNum) const { return wfSSG_.at(static_cast(wfNum))->getIterator(); } -std::vector InstrumentsManager::getWaveformSSGUsers(int wfNum) const +std::multiset InstrumentsManager::getWaveformSSGUsers(int wfNum) const { return wfSSG_.at(static_cast(wfNum))->getUserInstruments(); } std::vector InstrumentsManager::getWaveformSSGEntriedIndices() const { - std::vector idcs; - int n = 0; - for (auto& wf : wfSSG_) { - if (wf->isUserInstrument() || wf->isEdited()) idcs.push_back(n); - ++n; - } - return idcs; + return utils::findIndicesIf(wfSSG_, IsUsedOrEdited()); } int InstrumentsManager::findFirstAssignableWaveformSSG() const { - std::function&)> cond; - if (regardingUnedited_) - cond = [](const std::shared_ptr& wf) { return (wf->isUserInstrument() || wf->isEdited()); }; - else - cond = [](const std::shared_ptr& wf) { return wf->isUserInstrument(); }; - auto&& it = std::find_if_not(wfSSG_.begin(), wfSSG_.end(), cond); - - if (it == wfSSG_.end()) return -1; - - if (!regardingUnedited_) (*it)->clearParameters(); - return std::distance(wfSSG_.begin(), it); + return findFirstAssignableProperty(wfSSG_, regardingUnedited_); } void InstrumentsManager::setInstrumentSSGToneNoiseEnabled(int instNum, bool enabled) @@ -1553,83 +1384,82 @@ int InstrumentsManager::getInstrumentSSGToneNoise(int instNum) { - return std::dynamic_pointer_cast(insts_[static_cast(instNum)])->getToneNoiseNumber(); + return std::dynamic_pointer_cast(insts_.at(static_cast(instNum)))->getToneNoiseNumber(); } -void InstrumentsManager::addToneNoiseSSGSequenceCommand(int tnNum, int type, int data) +void InstrumentsManager::addToneNoiseSSGSequenceData(int tnNum, int data) { - tnSSG_.at(static_cast(tnNum))->addSequenceCommand(type, data); + tnSSG_.at(static_cast(tnNum))->addSequenceUnit(SSGToneNoiseUnit(data)); } -void InstrumentsManager::removeToneNoiseSSGSequenceCommand(int tnNum) +void InstrumentsManager::removeToneNoiseSSGSequenceData(int tnNum) { - tnSSG_.at(static_cast(tnNum))->removeSequenceCommand(); + tnSSG_.at(static_cast(tnNum))->removeSequenceUnit(); } -void InstrumentsManager::setToneNoiseSSGSequenceCommand(int tnNum, int cnt, int type, int data) +void InstrumentsManager::setToneNoiseSSGSequenceData(int tnNum, int cnt, int data) { - tnSSG_.at(static_cast(tnNum))->setSequenceCommand(cnt, type, data); + tnSSG_.at(static_cast(tnNum))->setSequenceUnit(cnt, SSGToneNoiseUnit(data)); } -std::vector InstrumentsManager::getToneNoiseSSGSequence(int tnNum) +std::vector InstrumentsManager::getToneNoiseSSGSequence(int tnNum) { return tnSSG_.at(static_cast(tnNum))->getSequence(); } -void InstrumentsManager::setToneNoiseSSGLoops(int tnNum, std::vector begins, std::vector ends, std::vector times) +void InstrumentsManager::addToneNoiseSSGLoop(int tnNum, const InstrumentSequenceLoop& loop) +{ + tnSSG_.at(static_cast(tnNum))->addLoop(loop); +} + +void InstrumentsManager::removeToneNoiseSSGLoop(int tnNum, int begin, int end) +{ + tnSSG_.at(static_cast(tnNum))->removeLoop(begin, end); +} + +void InstrumentsManager::changeToneNoiseSSGLoop(int tnNum, int prevBegin, int prevEnd, const InstrumentSequenceLoop& loop) { - tnSSG_.at(static_cast(tnNum))->setLoops(std::move(begins), std::move(ends), std::move(times)); + tnSSG_.at(static_cast(tnNum))->changeLoop(prevBegin, prevEnd, loop); } -std::vector InstrumentsManager::getToneNoiseSSGLoops(int tnNum) const +void InstrumentsManager::clearToneNoiseSSGLoops(int tnNum) { - return tnSSG_.at(static_cast(tnNum))->getLoops(); + tnSSG_.at(static_cast(tnNum))->clearLoops(); } -void InstrumentsManager::setToneNoiseSSGRelease(int tnNum, ReleaseType type, int begin) +InstrumentSequenceLoopRoot InstrumentsManager::getToneNoiseSSGLoopRoot(int tnNum) const { - tnSSG_.at(static_cast(tnNum))->setRelease(type, begin); + return tnSSG_.at(static_cast(tnNum))->getLoopRoot(); } -Release InstrumentsManager::getToneNoiseSSGRelease(int tnNum) const +void InstrumentsManager::setToneNoiseSSGRelease(int tnNum, const InstrumentSequenceRelease& release) +{ + tnSSG_.at(static_cast(tnNum))->setRelease(release); +} + +InstrumentSequenceRelease InstrumentsManager::getToneNoiseSSGRelease(int tnNum) const { return tnSSG_.at(static_cast(tnNum))->getRelease(); } -std::unique_ptr InstrumentsManager::getToneNoiseSSGIterator(int tnNum) const +SSGToneNoiseIter InstrumentsManager::getToneNoiseSSGIterator(int tnNum) const { return tnSSG_.at(static_cast(tnNum))->getIterator(); } -std::vector InstrumentsManager::getToneNoiseSSGUsers(int tnNum) const +std::multiset InstrumentsManager::getToneNoiseSSGUsers(int tnNum) const { return tnSSG_.at(static_cast(tnNum))->getUserInstruments(); } std::vector InstrumentsManager::getToneNoiseSSGEntriedIndices() const { - std::vector idcs; - int n = 0; - for (auto& tn : tnSSG_) { - if (tn->isUserInstrument() || tn->isEdited()) idcs.push_back(n); - ++n; - } - return idcs; + return utils::findIndicesIf(tnSSG_, IsUsedOrEdited()); } int InstrumentsManager::findFirstAssignableToneNoiseSSG() const { - std::function&)> cond; - if (regardingUnedited_) - cond = [](const std::shared_ptr& tn) { return (tn->isUserInstrument() || tn->isEdited()); }; - else - cond = [](const std::shared_ptr& tn) { return tn->isUserInstrument(); }; - auto&& it = std::find_if_not(tnSSG_.begin(), tnSSG_.end(), cond); - - if (it == tnSSG_.end()) return -1; - - if (!regardingUnedited_) (*it)->clearParameters(); - return std::distance(tnSSG_.begin(), it); + return findFirstAssignableProperty(tnSSG_, regardingUnedited_); } void InstrumentsManager::setInstrumentSSGEnvelopeEnabled(int instNum, bool enabled) @@ -1659,83 +1489,82 @@ int InstrumentsManager::getInstrumentSSGEnvelope(int instNum) { - return std::dynamic_pointer_cast(insts_[static_cast(instNum)])->getEnvelopeNumber(); + return std::dynamic_pointer_cast(insts_.at(static_cast(instNum)))->getEnvelopeNumber(); } -void InstrumentsManager::addEnvelopeSSGSequenceCommand(int envNum, int type, int data) +void InstrumentsManager::addEnvelopeSSGSequenceData(int envNum, const SSGEnvelopeUnit& data) { - envSSG_.at(static_cast(envNum))->addSequenceCommand(type, data); + envSSG_.at(static_cast(envNum))->addSequenceUnit(data); } -void InstrumentsManager::removeEnvelopeSSGSequenceCommand(int envNum) +void InstrumentsManager::removeEnvelopeSSGSequenceData(int envNum) { - envSSG_.at(static_cast(envNum))->removeSequenceCommand(); + envSSG_.at(static_cast(envNum))->removeSequenceUnit(); } -void InstrumentsManager::setEnvelopeSSGSequenceCommand(int envNum, int cnt, int type, int data) +void InstrumentsManager::setEnvelopeSSGSequenceData(int envNum, int cnt, const SSGEnvelopeUnit& data) { - envSSG_.at(static_cast(envNum))->setSequenceCommand(cnt, type, data); + envSSG_.at(static_cast(envNum))->setSequenceUnit(cnt, data); } -std::vector InstrumentsManager::getEnvelopeSSGSequence(int envNum) +std::vector InstrumentsManager::getEnvelopeSSGSequence(int envNum) { return envSSG_.at(static_cast(envNum))->getSequence(); } -void InstrumentsManager::setEnvelopeSSGLoops(int envNum, std::vector begins, std::vector ends, std::vector times) +void InstrumentsManager::addEnvelopeSSGLoop(int envNum, const InstrumentSequenceLoop& loop) +{ + envSSG_.at(static_cast(envNum))->addLoop(loop); +} + +void InstrumentsManager::removeEnvelopeSSGLoop(int envNum, int begin, int end) +{ + envSSG_.at(static_cast(envNum))->removeLoop(begin, end); +} + +void InstrumentsManager::changeEnvelopeSSGLoop(int envNum, int prevBegin, int prevEnd, const InstrumentSequenceLoop& loop) { - envSSG_.at(static_cast(envNum))->setLoops(std::move(begins), std::move(ends), std::move(times)); + envSSG_.at(static_cast(envNum))->changeLoop(prevBegin, prevEnd, loop); } -std::vector InstrumentsManager::getEnvelopeSSGLoops(int envNum) const +void InstrumentsManager::clearEnvelopeSSGLoops(int envNum) { - return envSSG_.at(static_cast(envNum))->getLoops(); + envSSG_.at(static_cast(envNum))->clearLoops(); } -void InstrumentsManager::setEnvelopeSSGRelease(int envNum, ReleaseType type, int begin) +InstrumentSequenceLoopRoot InstrumentsManager::getEnvelopeSSGLoopRoot(int envNum) const { - envSSG_.at(static_cast(envNum))->setRelease(type, begin); + return envSSG_.at(static_cast(envNum))->getLoopRoot(); } -Release InstrumentsManager::getEnvelopeSSGRelease(int envNum) const +void InstrumentsManager::setEnvelopeSSGRelease(int envNum, const InstrumentSequenceRelease& release) +{ + envSSG_.at(static_cast(envNum))->setRelease(release); +} + +InstrumentSequenceRelease InstrumentsManager::getEnvelopeSSGRelease(int envNum) const { return envSSG_.at(static_cast(envNum))->getRelease(); } -std::unique_ptr InstrumentsManager::getEnvelopeSSGIterator(int envNum) const +SSGEnvelopeIter InstrumentsManager::getEnvelopeSSGIterator(int envNum) const { return envSSG_.at(static_cast(envNum))->getIterator(); } -std::vector InstrumentsManager::getEnvelopeSSGUsers(int envNum) const +std::multiset InstrumentsManager::getEnvelopeSSGUsers(int envNum) const { return envSSG_.at(static_cast(envNum))->getUserInstruments(); } std::vector InstrumentsManager::getEnvelopeSSGEntriedIndices() const { - std::vector idcs; - int n = 0; - for (auto& env : envSSG_) { - if (env->isUserInstrument() || env->isEdited()) idcs.push_back(n); - ++n; - } - return idcs; + return utils::findIndicesIf(envSSG_, IsUsedOrEdited()); } int InstrumentsManager::findFirstAssignableEnvelopeSSG() const { - std::function&)> cond; - if (regardingUnedited_) - cond = [](const std::shared_ptr& env) { return (env->isUserInstrument() || env->isEdited()); }; - else - cond = [](const std::shared_ptr& env) { return env->isUserInstrument(); }; - auto&& it = std::find_if_not(envSSG_.begin(), envSSG_.end(), cond); - - if (it == envSSG_.end()) return -1; - - if (!regardingUnedited_) (*it)->clearParameters(); - return std::distance(envSSG_.begin(), it); + return findFirstAssignableProperty(envSSG_, regardingUnedited_); } void InstrumentsManager::setInstrumentSSGArpeggioEnabled(int instNum, bool enabled) @@ -1765,7 +1594,7 @@ int InstrumentsManager::getInstrumentSSGArpeggio(int instNum) { - return std::dynamic_pointer_cast(insts_[static_cast(instNum)])->getArpeggioNumber(); + return std::dynamic_pointer_cast(insts_.at(static_cast(instNum)))->getArpeggioNumber(); } void InstrumentsManager::setArpeggioSSGType(int arpNum, SequenceType type) @@ -1778,80 +1607,79 @@ return arpSSG_.at(static_cast(arpNum))->getType(); } -void InstrumentsManager::addArpeggioSSGSequenceCommand(int arpNum, int type, int data) +void InstrumentsManager::addArpeggioSSGSequenceData(int arpNum, int data) { - arpSSG_.at(static_cast(arpNum))->addSequenceCommand(type, data); + arpSSG_.at(static_cast(arpNum))->addSequenceUnit(ArpeggioUnit(data)); } -void InstrumentsManager::removeArpeggioSSGSequenceCommand(int arpNum) +void InstrumentsManager::removeArpeggioSSGSequenceData(int arpNum) { - arpSSG_.at(static_cast(arpNum))->removeSequenceCommand(); + arpSSG_.at(static_cast(arpNum))->removeSequenceUnit(); } -void InstrumentsManager::setArpeggioSSGSequenceCommand(int arpNum, int cnt, int type, int data) +void InstrumentsManager::setArpeggioSSGSequenceData(int arpNum, int cnt, int data) { - arpSSG_.at(static_cast(arpNum))->setSequenceCommand(cnt, type, data); + arpSSG_.at(static_cast(arpNum))->setSequenceUnit(cnt, ArpeggioUnit(data)); } -std::vector InstrumentsManager::getArpeggioSSGSequence(int arpNum) +std::vector InstrumentsManager::getArpeggioSSGSequence(int arpNum) { return arpSSG_.at(static_cast(arpNum))->getSequence(); } -void InstrumentsManager::setArpeggioSSGLoops(int arpNum, std::vector begins, std::vector ends, std::vector times) +void InstrumentsManager::addArpeggioSSGLoop(int arpNum, const InstrumentSequenceLoop& loop) { - arpSSG_.at(static_cast(arpNum))->setLoops(std::move(begins), std::move(ends), std::move(times)); + arpSSG_.at(static_cast(arpNum))->addLoop(loop); } -std::vector InstrumentsManager::getArpeggioSSGLoops(int arpNum) const +void InstrumentsManager::removeArpeggioSSGLoop(int arpNum, int begin, int end) { - return arpSSG_.at(static_cast(arpNum))->getLoops(); + arpSSG_.at(static_cast(arpNum))->removeLoop(begin, end); } -void InstrumentsManager::setArpeggioSSGRelease(int arpNum, ReleaseType type, int begin) +void InstrumentsManager::changeArpeggioSSGLoop(int arpNum, int prevBegin, int prevEnd, const InstrumentSequenceLoop& loop) { - arpSSG_.at(static_cast(arpNum))->setRelease(type, begin); + arpSSG_.at(static_cast(arpNum))->changeLoop(prevBegin, prevEnd, loop); } -Release InstrumentsManager::getArpeggioSSGRelease(int arpNum) const +void InstrumentsManager::clearArpeggioSSGLoops(int arpNum) +{ + arpSSG_.at(static_cast(arpNum))->clearLoops(); +} + +InstrumentSequenceLoopRoot InstrumentsManager::getArpeggioSSGLoopRoot(int arpNum) const +{ + return arpSSG_.at(static_cast(arpNum))->getLoopRoot(); +} + +void InstrumentsManager::setArpeggioSSGRelease(int arpNum, const InstrumentSequenceRelease& release) +{ + arpSSG_.at(static_cast(arpNum))->setRelease(release); +} + +InstrumentSequenceRelease InstrumentsManager::getArpeggioSSGRelease(int arpNum) const { return arpSSG_.at(static_cast(arpNum))->getRelease(); } -std::unique_ptr InstrumentsManager::getArpeggioSSGIterator(int arpNum) const +ArpeggioIter InstrumentsManager::getArpeggioSSGIterator(int arpNum) const { return arpSSG_.at(static_cast(arpNum))->getIterator(); } -std::vector InstrumentsManager::getArpeggioSSGUsers(int arpNum) const +std::multiset InstrumentsManager::getArpeggioSSGUsers(int arpNum) const { return arpSSG_.at(static_cast(arpNum))->getUserInstruments(); } std::vector InstrumentsManager::getArpeggioSSGEntriedIndices() const { - std::vector idcs; - int n = 0; - for (auto& arp : arpSSG_) { - if (arp->isUserInstrument() || arp->isEdited()) idcs.push_back(n); - ++n; - } - return idcs; + return utils::findIndicesIf(arpSSG_, IsUsedOrEdited()); } int InstrumentsManager::findFirstAssignableArpeggioSSG() const { - std::function&)> cond; - if (regardingUnedited_) - cond = [](const std::shared_ptr& arp) { return (arp->isUserInstrument() || arp->isEdited()); }; - else - cond = [](const std::shared_ptr& arp) { return arp->isUserInstrument(); }; - auto&& it = std::find_if_not(arpSSG_.begin(), arpSSG_.end(), cond); - - if (it == arpSSG_.end()) return -1; - - if (!regardingUnedited_) (*it)->clearParameters(); - return std::distance(arpSSG_.begin(), it); + return findFirstAssignableProperty(arpSSG_, regardingUnedited_); } void InstrumentsManager::setInstrumentSSGPitchEnabled(int instNum, bool enabled) @@ -1881,7 +1709,7 @@ int InstrumentsManager::getInstrumentSSGPitch(int instNum) { - return std::dynamic_pointer_cast(insts_[static_cast(instNum)])->getPitchNumber(); + return std::dynamic_pointer_cast(insts_.at(static_cast(instNum)))->getPitchNumber(); } void InstrumentsManager::setPitchSSGType(int ptNum, SequenceType type) @@ -1894,80 +1722,79 @@ return ptSSG_.at(static_cast(ptNum))->getType(); } -void InstrumentsManager::addPitchSSGSequenceCommand(int ptNum, int type, int data) +void InstrumentsManager::addPitchSSGSequenceData(int ptNum, int data) { - ptSSG_.at(static_cast(ptNum))->addSequenceCommand(type, data); + ptSSG_.at(static_cast(ptNum))->addSequenceUnit(PitchUnit(data)); } -void InstrumentsManager::removePitchSSGSequenceCommand(int ptNum) +void InstrumentsManager::removePitchSSGSequenceData(int ptNum) { - ptSSG_.at(static_cast(ptNum))->removeSequenceCommand(); + ptSSG_.at(static_cast(ptNum))->removeSequenceUnit(); } -void InstrumentsManager::setPitchSSGSequenceCommand(int ptNum, int cnt, int type, int data) +void InstrumentsManager::setPitchSSGSequenceData(int ptNum, int cnt, int data) { - ptSSG_.at(static_cast(ptNum))->setSequenceCommand(cnt, type, data); + ptSSG_.at(static_cast(ptNum))->setSequenceUnit(cnt, PitchUnit(data)); } -std::vector InstrumentsManager::getPitchSSGSequence(int ptNum) +std::vector InstrumentsManager::getPitchSSGSequence(int ptNum) { return ptSSG_.at(static_cast(ptNum))->getSequence(); } -void InstrumentsManager::setPitchSSGLoops(int ptNum, std::vector begins, std::vector ends, std::vector times) +void InstrumentsManager::addPitchSSGLoop(int ptNum, const InstrumentSequenceLoop& loop) +{ + ptSSG_.at(static_cast(ptNum))->addLoop(loop); +} + +void InstrumentsManager::removePitchSSGLoop(int ptNum, int begin, int end) +{ + ptSSG_.at(static_cast(ptNum))->removeLoop(begin, end); +} + +void InstrumentsManager::changePitchSSGLoop(int ptNum, int prevBegin, int prevEnd, const InstrumentSequenceLoop& loop) +{ + ptSSG_.at(static_cast(ptNum))->changeLoop(prevBegin, prevEnd, loop); +} + +void InstrumentsManager::clearPitchSSGLoops(int ptNum) { - ptSSG_.at(static_cast(ptNum))->setLoops(std::move(begins), std::move(ends), std::move(times)); + ptSSG_.at(static_cast(ptNum))->clearLoops(); } -std::vector InstrumentsManager::getPitchSSGLoops(int ptNum) const +InstrumentSequenceLoopRoot InstrumentsManager::getPitchSSGLoopRoot(int ptNum) const { - return ptSSG_.at(static_cast(ptNum))->getLoops(); + return ptSSG_.at(static_cast(ptNum))->getLoopRoot(); } -void InstrumentsManager::setPitchSSGRelease(int ptNum, ReleaseType type, int begin) +void InstrumentsManager::setPitchSSGRelease(int ptNum, const InstrumentSequenceRelease& release) { - ptSSG_.at(static_cast(ptNum))->setRelease(type, begin); + ptSSG_.at(static_cast(ptNum))->setRelease(release); } -Release InstrumentsManager::getPitchSSGRelease(int ptNum) const +InstrumentSequenceRelease InstrumentsManager::getPitchSSGRelease(int ptNum) const { return ptSSG_.at(static_cast(ptNum))->getRelease(); } -std::unique_ptr InstrumentsManager::getPitchSSGIterator(int ptNum) const +PitchIter InstrumentsManager::getPitchSSGIterator(int ptNum) const { return ptSSG_.at(static_cast(ptNum))->getIterator(); } -std::vector InstrumentsManager::getPitchSSGUsers(int ptNum) const +std::multiset InstrumentsManager::getPitchSSGUsers(int ptNum) const { return ptSSG_.at(static_cast(ptNum))->getUserInstruments(); } std::vector InstrumentsManager::getPitchSSGEntriedIndices() const { - std::vector idcs; - int n = 0; - for (auto& pt : ptSSG_) { - if (pt->isUserInstrument() || pt->isEdited()) idcs.push_back(n); - ++n; - } - return idcs; + return utils::findIndicesIf(ptSSG_, IsUsedOrEdited()); } int InstrumentsManager::findFirstAssignablePitchSSG() const { - std::function&)> cond; - if (regardingUnedited_) - cond = [](const std::shared_ptr& pt) { return (pt->isUserInstrument() || pt->isEdited()); }; - else - cond = [](const std::shared_ptr& pt) { return pt->isUserInstrument(); }; - auto&& it = std::find_if_not(ptSSG_.begin(), ptSSG_.end(), cond); - - if (it == ptSSG_.end()) return -1; - - if (!regardingUnedited_) (*it)->clearParameters(); - return std::distance(ptSSG_.begin(), it); + return findFirstAssignableProperty(ptSSG_, regardingUnedited_); } bool InstrumentsManager::equalPropertiesSSG(std::shared_ptr a, std::shared_ptr b) const @@ -1978,27 +1805,27 @@ if (aSsg->getWaveformEnabled() != bSsg->getWaveformEnabled()) return false; if (aSsg->getWaveformEnabled() - && *wfSSG_[aSsg->getWaveformNumber()].get() != *wfSSG_[bSsg->getWaveformNumber()].get()) + && *wfSSG_[aSsg->getWaveformNumber()] != *wfSSG_[bSsg->getWaveformNumber()]) return false; if (aSsg->getToneNoiseEnabled() != bSsg->getToneNoiseEnabled()) return false; if (aSsg->getToneNoiseEnabled() - && *tnSSG_[aSsg->getToneNoiseNumber()].get() != *tnSSG_[bSsg->getToneNoiseNumber()].get()) + && *tnSSG_[aSsg->getToneNoiseNumber()] != *tnSSG_[bSsg->getToneNoiseNumber()]) return false; if (aSsg->getEnvelopeEnabled() != bSsg->getEnvelopeEnabled()) return false; if (aSsg->getEnvelopeEnabled() - && *envSSG_[aSsg->getEnvelopeNumber()].get() != *envSSG_[bSsg->getEnvelopeNumber()].get()) + && *envSSG_[aSsg->getEnvelopeNumber()] != *envSSG_[bSsg->getEnvelopeNumber()]) return false; if (aSsg->getArpeggioEnabled() != bSsg->getArpeggioEnabled()) return false; if (aSsg->getArpeggioEnabled() - && *arpSSG_[aSsg->getArpeggioNumber()].get() != *arpSSG_[bSsg->getArpeggioNumber()].get()) + && *arpSSG_[aSsg->getArpeggioNumber()] != *arpSSG_[bSsg->getArpeggioNumber()]) return false; if (aSsg->getPitchEnabled() != bSsg->getPitchEnabled()) return false; if (aSsg->getPitchEnabled() - && *ptSSG_[aSsg->getPitchNumber()].get() != *ptSSG_[bSsg->getPitchNumber()].get()) + && *ptSSG_[aSsg->getPitchNumber()] != *ptSSG_[bSsg->getPitchNumber()]) return false; return true; } @@ -2015,7 +1842,7 @@ int InstrumentsManager::getInstrumentADPCMSample(int instNum) { - return std::dynamic_pointer_cast(insts_[static_cast(instNum)])->getSampleNumber(); + return std::dynamic_pointer_cast(insts_.at(static_cast(instNum)))->getSampleNumber(); } void InstrumentsManager::setSampleADPCMRootKeyNumber(int sampNum, int n) @@ -2048,7 +1875,12 @@ return sampADPCM_.at(static_cast(sampNum))->isRepeatable(); } -void InstrumentsManager::storeSampleADPCMRawSample(int sampNum, std::vector sample) +void InstrumentsManager::storeSampleADPCMRawSample(int sampNum, const std::vector& sample) +{ + sampADPCM_.at(static_cast(sampNum))->storeSample(sample); +} + +void InstrumentsManager::storeSampleADPCMRawSample(int sampNum, std::vector&& sample) { sampADPCM_.at(static_cast(sampNum))->storeSample(sample); } @@ -2083,31 +1915,19 @@ return sampADPCM_.at(static_cast(sampNum))->getStopAddress(); } -std::vector InstrumentsManager::getSampleADPCMUsers(int sampNum) const +std::multiset InstrumentsManager::getSampleADPCMUsers(int sampNum) const { return sampADPCM_.at(static_cast(sampNum))->getUserInstruments(); } std::vector InstrumentsManager::getSampleADPCMEntriedIndices() const { - std::vector idcs; - int n = 0; - for (auto& samp : sampADPCM_) { - if (samp->isEdited()) idcs.push_back(n); - ++n; - } - return idcs; + return utils::findIndicesIf(sampADPCM_, IsEdited()); } std::vector InstrumentsManager::getSampleADPCMValidIndices() const { - std::vector idcs; - int n = 0; - for (auto& samp : sampADPCM_) { - if (samp->isUserInstrument() && samp->isEdited()) idcs.push_back(n); - ++n; - } - return idcs; + return utils::findIndicesIf(sampADPCM_, IsUsedOrEdited()); } void InstrumentsManager::clearUnusedSamplesADPCM() @@ -2120,17 +1940,7 @@ int InstrumentsManager::findFirstAssignableSampleADPCM(int startIndex) const { - std::function&)> cond; - if (regardingUnedited_) - cond = [](const std::shared_ptr& samp) { return (samp->isUserInstrument() || samp->isEdited()); }; - else - cond = [](const std::shared_ptr& samp) { return samp->isUserInstrument(); }; - auto&& it = std::find_if_not(sampADPCM_.begin() + startIndex, sampADPCM_.end(), cond); - - if (it == sampADPCM_.end()) return -1; - - if (!regardingUnedited_) (*it)->clearParameters(); - return std::distance(sampADPCM_.begin(), it); + return findFirstAssignableProperty(sampADPCM_, regardingUnedited_, startIndex); } void InstrumentsManager::setInstrumentADPCMEnvelopeEnabled(int instNum, bool enabled) @@ -2160,83 +1970,82 @@ int InstrumentsManager::getInstrumentADPCMEnvelope(int instNum) { - return std::dynamic_pointer_cast(insts_[static_cast(instNum)])->getEnvelopeNumber(); + return std::dynamic_pointer_cast(insts_.at(static_cast(instNum)))->getEnvelopeNumber(); } -void InstrumentsManager::addEnvelopeADPCMSequenceCommand(int envNum, int type, int data) +void InstrumentsManager::addEnvelopeADPCMSequenceData(int envNum, int data) { - envADPCM_.at(static_cast(envNum))->addSequenceCommand(type, data); + envADPCM_.at(static_cast(envNum))->addSequenceUnit(ADPCMEnvelopeUnit(data)); } -void InstrumentsManager::removeEnvelopeADPCMSequenceCommand(int envNum) +void InstrumentsManager::removeEnvelopeADPCMSequenceData(int envNum) { - envADPCM_.at(static_cast(envNum))->removeSequenceCommand(); + envADPCM_.at(static_cast(envNum))->removeSequenceUnit(); } -void InstrumentsManager::setEnvelopeADPCMSequenceCommand(int envNum, int cnt, int type, int data) +void InstrumentsManager::setEnvelopeADPCMSequenceData(int envNum, int cnt, int data) { - envADPCM_.at(static_cast(envNum))->setSequenceCommand(cnt, type, data); + envADPCM_.at(static_cast(envNum))->setSequenceUnit(cnt, ADPCMEnvelopeUnit(data)); } -std::vector InstrumentsManager::getEnvelopeADPCMSequence(int envNum) +std::vector InstrumentsManager::getEnvelopeADPCMSequence(int envNum) { return envADPCM_.at(static_cast(envNum))->getSequence(); } -void InstrumentsManager::setEnvelopeADPCMLoops(int envNum, std::vector begins, std::vector ends, std::vector times) +void InstrumentsManager::addEnvelopeADPCMLoop(int envNum, const InstrumentSequenceLoop& loop) +{ + envADPCM_.at(static_cast(envNum))->addLoop(loop); +} + +void InstrumentsManager::removeEnvelopeADPCMLoop(int envNum, int begin, int end) +{ + envADPCM_.at(static_cast(envNum))->removeLoop(begin, end); +} + +void InstrumentsManager::changeEnvelopeADPCMLoop(int envNum, int prevBegin, int prevEnd, const InstrumentSequenceLoop& loop) { - envADPCM_.at(static_cast(envNum))->setLoops(std::move(begins), std::move(ends), std::move(times)); + envADPCM_.at(static_cast(envNum))->changeLoop(prevBegin, prevEnd, loop); } -std::vector InstrumentsManager::getEnvelopeADPCMLoops(int envNum) const +void InstrumentsManager::clearEnvelopeADPCMLoops(int envNum) { - return envADPCM_.at(static_cast(envNum))->getLoops(); + envADPCM_.at(static_cast(envNum))->clearLoops(); } -void InstrumentsManager::setEnvelopeADPCMRelease(int envNum, ReleaseType type, int begin) +InstrumentSequenceLoopRoot InstrumentsManager::getEnvelopeADPCMLoopRoot(int envNum) const { - envADPCM_.at(static_cast(envNum))->setRelease(type, begin); + return envADPCM_.at(static_cast(envNum))->getLoopRoot(); } -Release InstrumentsManager::getEnvelopeADPCMRelease(int envNum) const +void InstrumentsManager::setEnvelopeADPCMRelease(int envNum, const InstrumentSequenceRelease& release) +{ + envADPCM_.at(static_cast(envNum))->setRelease(release); +} + +InstrumentSequenceRelease InstrumentsManager::getEnvelopeADPCMRelease(int envNum) const { return envADPCM_.at(static_cast(envNum))->getRelease(); } -std::unique_ptr InstrumentsManager::getEnvelopeADPCMIterator(int envNum) const +ADPCMEnvelopeIter InstrumentsManager::getEnvelopeADPCMIterator(int envNum) const { return envADPCM_.at(static_cast(envNum))->getIterator(); } -std::vector InstrumentsManager::getEnvelopeADPCMUsers(int envNum) const +std::multiset InstrumentsManager::getEnvelopeADPCMUsers(int envNum) const { return envADPCM_.at(static_cast(envNum))->getUserInstruments(); } std::vector InstrumentsManager::getEnvelopeADPCMEntriedIndices() const { - std::vector idcs; - int n = 0; - for (auto& env : envADPCM_) { - if (env->isUserInstrument() || env->isEdited()) idcs.push_back(n); - ++n; - } - return idcs; + return utils::findIndicesIf(envADPCM_, IsUsedOrEdited()); } int InstrumentsManager::findFirstAssignableEnvelopeADPCM() const { - std::function&)> cond; - if (regardingUnedited_) - cond = [](const std::shared_ptr& env) { return (env->isUserInstrument() || env->isEdited()); }; - else - cond = [](const std::shared_ptr& env) { return env->isUserInstrument(); }; - auto&& it = std::find_if_not(envADPCM_.begin(), envADPCM_.end(), cond); - - if (it == envADPCM_.end()) return -1; - - if (!regardingUnedited_) (*it)->clearParameters(); - return std::distance(envADPCM_.begin(), it); + return findFirstAssignableProperty(envADPCM_, regardingUnedited_); } void InstrumentsManager::setInstrumentADPCMArpeggioEnabled(int instNum, bool enabled) @@ -2266,7 +2075,7 @@ int InstrumentsManager::getInstrumentADPCMArpeggio(int instNum) { - return std::dynamic_pointer_cast(insts_[static_cast(instNum)])->getArpeggioNumber(); + return std::dynamic_pointer_cast(insts_.at(static_cast(instNum)))->getArpeggioNumber(); } void InstrumentsManager::setArpeggioADPCMType(int arpNum, SequenceType type) @@ -2279,80 +2088,79 @@ return arpADPCM_.at(static_cast(arpNum))->getType(); } -void InstrumentsManager::addArpeggioADPCMSequenceCommand(int arpNum, int type, int data) +void InstrumentsManager::addArpeggioADPCMSequenceData(int arpNum, int data) { - arpADPCM_.at(static_cast(arpNum))->addSequenceCommand(type, data); + arpADPCM_.at(static_cast(arpNum))->addSequenceUnit(ArpeggioUnit(data)); } -void InstrumentsManager::removeArpeggioADPCMSequenceCommand(int arpNum) +void InstrumentsManager::removeArpeggioADPCMSequenceData(int arpNum) { - arpADPCM_.at(static_cast(arpNum))->removeSequenceCommand(); + arpADPCM_.at(static_cast(arpNum))->removeSequenceUnit(); } -void InstrumentsManager::setArpeggioADPCMSequenceCommand(int arpNum, int cnt, int type, int data) +void InstrumentsManager::setArpeggioADPCMSequenceData(int arpNum, int cnt, int data) { - arpADPCM_.at(static_cast(arpNum))->setSequenceCommand(cnt, type, data); + arpADPCM_.at(static_cast(arpNum))->setSequenceUnit(cnt, ArpeggioUnit(data)); } -std::vector InstrumentsManager::getArpeggioADPCMSequence(int arpNum) +std::vector InstrumentsManager::getArpeggioADPCMSequence(int arpNum) { return arpADPCM_.at(static_cast(arpNum))->getSequence(); } -void InstrumentsManager::setArpeggioADPCMLoops(int arpNum, std::vector begins, std::vector ends, std::vector times) +void InstrumentsManager::addArpeggioADPCMLoop(int arpNum, const InstrumentSequenceLoop& loop) +{ + arpADPCM_.at(static_cast(arpNum))->addLoop(loop); +} + +void InstrumentsManager::removeArpeggioADPCMLoop(int arpNum, int begin, int end) +{ + arpADPCM_.at(static_cast(arpNum))->removeLoop(begin, end); +} + +void InstrumentsManager::changeArpeggioADPCMLoop(int arpNum, int prevBegin, int prevEnd, const InstrumentSequenceLoop& loop) { - arpADPCM_.at(static_cast(arpNum))->setLoops(std::move(begins), std::move(ends), std::move(times)); + arpADPCM_.at(static_cast(arpNum))->changeLoop(prevBegin, prevEnd, loop); } -std::vector InstrumentsManager::getArpeggioADPCMLoops(int arpNum) const +void InstrumentsManager::clearArpeggioADPCMLoops(int arpNum) { - return arpADPCM_.at(static_cast(arpNum))->getLoops(); + arpADPCM_.at(static_cast(arpNum))->clearLoops(); } -void InstrumentsManager::setArpeggioADPCMRelease(int arpNum, ReleaseType type, int begin) +InstrumentSequenceLoopRoot InstrumentsManager::getArpeggioADPCMLoopRoot(int arpNum) const { - arpADPCM_.at(static_cast(arpNum))->setRelease(type, begin); + return arpADPCM_.at(static_cast(arpNum))->getLoopRoot(); } -Release InstrumentsManager::getArpeggioADPCMRelease(int arpNum) const +void InstrumentsManager::setArpeggioADPCMRelease(int arpNum, const InstrumentSequenceRelease& release) +{ + arpADPCM_.at(static_cast(arpNum))->setRelease(release); +} + +InstrumentSequenceRelease InstrumentsManager::getArpeggioADPCMRelease(int arpNum) const { return arpADPCM_.at(static_cast(arpNum))->getRelease(); } -std::unique_ptr InstrumentsManager::getArpeggioADPCMIterator(int arpNum) const +ArpeggioIter InstrumentsManager::getArpeggioADPCMIterator(int arpNum) const { return arpADPCM_.at(static_cast(arpNum))->getIterator(); } -std::vector InstrumentsManager::getArpeggioADPCMUsers(int arpNum) const +std::multiset InstrumentsManager::getArpeggioADPCMUsers(int arpNum) const { return arpADPCM_.at(static_cast(arpNum))->getUserInstruments(); } std::vector InstrumentsManager::getArpeggioADPCMEntriedIndices() const { - std::vector idcs; - int n = 0; - for (auto& arp : arpADPCM_) { - if (arp->isUserInstrument() || arp->isEdited()) idcs.push_back(n); - ++n; - } - return idcs; + return utils::findIndicesIf(arpADPCM_, IsUsedOrEdited()); } int InstrumentsManager::findFirstAssignableArpeggioADPCM() const { - std::function&)> cond; - if (regardingUnedited_) - cond = [](const std::shared_ptr& arp) { return (arp->isUserInstrument() || arp->isEdited()); }; - else - cond = [](const std::shared_ptr& arp) { return arp->isUserInstrument(); }; - auto&& it = std::find_if_not(arpADPCM_.begin(), arpADPCM_.end(), cond); - - if (it == arpADPCM_.end()) return -1; - - if (!regardingUnedited_) (*it)->clearParameters(); - return std::distance(arpADPCM_.begin(), it); + return findFirstAssignableProperty(arpADPCM_, regardingUnedited_); } void InstrumentsManager::setInstrumentADPCMPitchEnabled(int instNum, bool enabled) @@ -2382,7 +2190,7 @@ int InstrumentsManager::getInstrumentADPCMPitch(int instNum) { - return std::dynamic_pointer_cast(insts_[static_cast(instNum)])->getPitchNumber(); + return std::dynamic_pointer_cast(insts_.at(static_cast(instNum)))->getPitchNumber(); } void InstrumentsManager::setPitchADPCMType(int ptNum, SequenceType type) @@ -2395,80 +2203,79 @@ return ptADPCM_.at(static_cast(ptNum))->getType(); } -void InstrumentsManager::addPitchADPCMSequenceCommand(int ptNum, int type, int data) +void InstrumentsManager::addPitchADPCMSequenceData(int ptNum, int data) { - ptADPCM_.at(static_cast(ptNum))->addSequenceCommand(type, data); + ptADPCM_.at(static_cast(ptNum))->addSequenceUnit(PitchUnit(data)); } -void InstrumentsManager::removePitchADPCMSequenceCommand(int ptNum) +void InstrumentsManager::removePitchADPCMSequenceData(int ptNum) { - ptADPCM_.at(static_cast(ptNum))->removeSequenceCommand(); + ptADPCM_.at(static_cast(ptNum))->removeSequenceUnit(); } -void InstrumentsManager::setPitchADPCMSequenceCommand(int ptNum, int cnt, int type, int data) +void InstrumentsManager::setPitchADPCMSequenceData(int ptNum, int cnt, int data) { - ptADPCM_.at(static_cast(ptNum))->setSequenceCommand(cnt, type, data); + ptADPCM_.at(static_cast(ptNum))->setSequenceUnit(cnt, PitchUnit(data)); } -std::vector InstrumentsManager::getPitchADPCMSequence(int ptNum) +std::vector InstrumentsManager::getPitchADPCMSequence(int ptNum) { return ptADPCM_.at(static_cast(ptNum))->getSequence(); } -void InstrumentsManager::setPitchADPCMLoops(int ptNum, std::vector begins, std::vector ends, std::vector times) +void InstrumentsManager::addPitchADPCMLoop(int ptNum, const InstrumentSequenceLoop& loop) { - ptADPCM_.at(static_cast(ptNum))->setLoops(std::move(begins), std::move(ends), std::move(times)); + ptADPCM_.at(static_cast(ptNum))->addLoop(loop); } -std::vector InstrumentsManager::getPitchADPCMLoops(int ptNum) const +void InstrumentsManager::removePitchADPCMLoop(int ptNum, int begin, int end) { - return ptADPCM_.at(static_cast(ptNum))->getLoops(); + ptADPCM_.at(static_cast(ptNum))->removeLoop(begin, end); } -void InstrumentsManager::setPitchADPCMRelease(int ptNum, ReleaseType type, int begin) +void InstrumentsManager::changePitchADPCMLoop(int ptNum, int prevBegin, int prevEnd, const InstrumentSequenceLoop& loop) { - ptADPCM_.at(static_cast(ptNum))->setRelease(type, begin); + ptADPCM_.at(static_cast(ptNum))->changeLoop(prevBegin, prevEnd, loop); } -Release InstrumentsManager::getPitchADPCMRelease(int ptNum) const +void InstrumentsManager::clearPitchADPCMLoops(int ptNum) +{ + ptADPCM_.at(static_cast(ptNum))->clearLoops(); +} + +InstrumentSequenceLoopRoot InstrumentsManager::getPitchADPCMLoopRoot(int ptNum) const +{ + return ptADPCM_.at(static_cast(ptNum))->getLoopRoot(); +} + +void InstrumentsManager::setPitchADPCMRelease(int ptNum, const InstrumentSequenceRelease& release) +{ + ptADPCM_.at(static_cast(ptNum))->setRelease(release); +} + +InstrumentSequenceRelease InstrumentsManager::getPitchADPCMRelease(int ptNum) const { return ptADPCM_.at(static_cast(ptNum))->getRelease(); } -std::unique_ptr InstrumentsManager::getPitchADPCMIterator(int ptNum) const +PitchIter InstrumentsManager::getPitchADPCMIterator(int ptNum) const { return ptADPCM_.at(static_cast(ptNum))->getIterator(); } -std::vector InstrumentsManager::getPitchADPCMUsers(int ptNum) const +std::multiset InstrumentsManager::getPitchADPCMUsers(int ptNum) const { return ptADPCM_.at(static_cast(ptNum))->getUserInstruments(); } std::vector InstrumentsManager::getPitchADPCMEntriedIndices() const { - std::vector idcs; - int n = 0; - for (auto& pt : ptADPCM_) { - if (pt->isUserInstrument() || pt->isEdited()) idcs.push_back(n); - ++n; - } - return idcs; + return utils::findIndicesIf(ptADPCM_, IsUsedOrEdited()); } int InstrumentsManager::findFirstAssignablePitchADPCM() const { - std::function&)> cond; - if (regardingUnedited_) - cond = [](const std::shared_ptr& pt) { return (pt->isUserInstrument() || pt->isEdited()); }; - else - cond = [](const std::shared_ptr& pt) { return pt->isUserInstrument(); }; - auto&& it = std::find_if_not(ptADPCM_.begin(), ptADPCM_.end(), cond); - - if (it == ptADPCM_.end()) return -1; - - if (!regardingUnedited_) (*it)->clearParameters(); - return std::distance(ptADPCM_.begin(), it); + return findFirstAssignableProperty(ptADPCM_, regardingUnedited_); } bool InstrumentsManager::equalPropertiesADPCM(std::shared_ptr a, std::shared_ptr b) const @@ -2476,22 +2283,22 @@ auto aAdpcm = std::dynamic_pointer_cast(a); auto bAdpcm = std::dynamic_pointer_cast(b); - if (*sampADPCM_[aAdpcm->getSampleNumber()].get() != *sampADPCM_[bAdpcm->getSampleNumber()].get()) + if (*sampADPCM_[aAdpcm->getSampleNumber()] != *sampADPCM_[bAdpcm->getSampleNumber()]) return false; if (aAdpcm->getEnvelopeEnabled() != bAdpcm->getEnvelopeEnabled()) return false; if (aAdpcm->getEnvelopeEnabled() - && *envADPCM_[aAdpcm->getEnvelopeNumber()].get() != *envADPCM_[bAdpcm->getEnvelopeNumber()].get()) + && *envADPCM_[aAdpcm->getEnvelopeNumber()] != *envADPCM_[bAdpcm->getEnvelopeNumber()]) return false; if (aAdpcm->getArpeggioEnabled() != bAdpcm->getArpeggioEnabled()) return false; if (aAdpcm->getArpeggioEnabled() - && *arpADPCM_[aAdpcm->getArpeggioNumber()].get() != *arpADPCM_[bAdpcm->getArpeggioNumber()].get()) + && *arpADPCM_[aAdpcm->getArpeggioNumber()] != *arpADPCM_[bAdpcm->getArpeggioNumber()]) return false; if (aAdpcm->getPitchEnabled() != bAdpcm->getPitchEnabled()) return false; if (aAdpcm->getPitchEnabled() - && *ptADPCM_[aAdpcm->getPitchNumber()].get() != *ptADPCM_[bAdpcm->getPitchNumber()].get()) + && *ptADPCM_[aAdpcm->getPitchNumber()] != *ptADPCM_[bAdpcm->getPitchNumber()]) return false; return true; } @@ -2526,7 +2333,7 @@ int InstrumentsManager::getInstrumentDrumkitSamples(int instNum, int key) { - return std::dynamic_pointer_cast(insts_[static_cast(instNum)])->getSampleNumber(key); + return std::dynamic_pointer_cast(insts_.at(static_cast(instNum)))->getSampleNumber(key); } void InstrumentsManager::setInstrumentDrumkitPitch(int instNum, int key, int pitch) @@ -2544,10 +2351,10 @@ if (aKeys.size() != bKeys.size()) return false; std::sort(aKeys.begin(), aKeys.end()); std::sort(bKeys.begin(), bKeys.end()); - if (!std::includes(aKeys.begin(), aKeys.end(), bKeys.begin(), bKeys.end())) return false; + if (!std::includes(aKeys.cbegin(), aKeys.cend(), bKeys.cbegin(), bKeys.cend())) return false; for (const int& key : aKeys) { - if (*sampADPCM_[aKit->getSampleNumber(key)].get() != *sampADPCM_[bKit->getSampleNumber(key)].get()) + if (*sampADPCM_[aKit->getSampleNumber(key)] != *sampADPCM_[bKit->getSampleNumber(key)]) return false; if (aKit->getPitch(key) != bKit->getPitch(key)) return false; diff -Nru bambootracker-0.4.5+git20121209/BambooTracker/instrument/instruments_manager.hpp bambootracker-0.4.6/BambooTracker/instrument/instruments_manager.hpp --- bambootracker-0.4.5+git20121209/BambooTracker/instrument/instruments_manager.hpp 2020-12-08 14:05:28.000000000 +0000 +++ bambootracker-0.4.6/BambooTracker/instrument/instruments_manager.hpp 2021-02-11 14:37:17.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (C) 2018-2020 Rerrah + * Copyright (C) 2018-2021 Rerrah * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation @@ -29,14 +29,14 @@ #include #include #include -#include +#include #include "instrument.hpp" #include "envelope_fm.hpp" #include "lfo_fm.hpp" -#include "waveform_adpcm.hpp" -#include "command_sequence.hpp" +#include "sample_adpcm.hpp" +#include "sequence_property.hpp" +#include "instrument_property_defs.hpp" #include "enum_hash.hpp" -#include "misc.hpp" enum class InstrumentType; class AbstractInstrument; @@ -52,8 +52,8 @@ public: explicit InstrumentsManager(bool unedited); - void addInstrument(int instNum, InstrumentType type, std::string name); - void addInstrument(std::unique_ptr inst); + void addInstrument(int instNum, InstrumentType type, const std::string& name); + void addInstrument(AbstractInstrument* newInstPtr); std::unique_ptr removeInstrument(int instNum); void cloneInstrument(int cloneInstNum, int resInstNum); void deepCloneInstrument(int cloneInstNum, int resInstNum); @@ -62,19 +62,17 @@ void clearAll(); std::vector getInstrumentIndices() const; - void setInstrumentName(int instNum, std::string name); + void setInstrumentName(int instNum, const std::string& name); std::string getInstrumentName(int instNum) const; std::vector getInstrumentNameList() const; - std::vector getEntriedInstrumentIndices() const; - void clearUnusedInstrumentProperties(); int findFirstFreeInstrument() const; - std::vector> checkDuplicateInstruments() const; + std::unordered_map getDuplicateInstrumentMap() const; - void setPropertyFindMode(bool unedited); + inline void setPropertyFindMode(bool unedited) noexcept { regardingUnedited_ = unedited; } private: std::array, 128> insts_; @@ -88,7 +86,7 @@ int getEnvelopeFMParameter(int envNum, FMEnvelopeParameter param) const; void setEnvelopeFMOperatorEnabled(int envNum, int opNum, bool enabled); bool getEnvelopeFMOperatorEnabled(int envNum, int opNum) const; - std::vector getEnvelopeFMUsers(int envNum) const; + std::multiset getEnvelopeFMUsers(int envNum) const; std::vector getEnvelopeFMEntriedIndices() const; int findFirstAssignableEnvelopeFM() const; @@ -98,7 +96,7 @@ int getInstrumentFMLFO(int instNum) const; void setLFOFMParameter(int lfoNum, FMLFOParameter param, int value); int getLFOFMparameter(int lfoNum, FMLFOParameter param) const; - std::vector getLFOFMUsers(int lfoNum) const; + std::multiset getLFOFMUsers(int lfoNum) const; std::vector getLFOFMEntriedIndices() const; int findFirstAssignableLFOFM() const; @@ -106,16 +104,19 @@ bool getInstrumentFMOperatorSequenceEnabled(int instNum, FMEnvelopeParameter param) const; void setInstrumentFMOperatorSequence(int instNum, FMEnvelopeParameter param, int opSeqNum); int getInstrumentFMOperatorSequence(int instNum, FMEnvelopeParameter param); - void addOperatorSequenceFMSequenceCommand(FMEnvelopeParameter param, int opSeqNum, int type, int data); - void removeOperatorSequenceFMSequenceCommand(FMEnvelopeParameter param, int opSeqNum); - void setOperatorSequenceFMSequenceCommand(FMEnvelopeParameter param, int opSeqNum, int cnt, int type, int data); - std::vector getOperatorSequenceFMSequence(FMEnvelopeParameter param, int opSeqNum); - void setOperatorSequenceFMLoops(FMEnvelopeParameter param, int opSeqNum, std::vector begins, std::vector ends, std::vector times); - std::vector getOperatorSequenceFMLoops(FMEnvelopeParameter param, int opSeqNum) const; - void setOperatorSequenceFMRelease(FMEnvelopeParameter param, int opSeqNum, ReleaseType type, int begin); - Release getOperatorSequenceFMRelease(FMEnvelopeParameter param, int opSeqNum) const; - std::unique_ptr getOperatorSequenceFMIterator(FMEnvelopeParameter param, int opSeqNum) const; - std::vector getOperatorSequenceFMUsers(FMEnvelopeParameter param, int opSeqNum) const; + void addOperatorSequenceFMSequenceData(FMEnvelopeParameter param, int opSeqNum, int data); + void removeOperatorSequenceFMSequenceData(FMEnvelopeParameter param, int opSeqNum); + void setOperatorSequenceFMSequenceData(FMEnvelopeParameter param, int opSeqNum, int cnt, int data); + std::vector getOperatorSequenceFMSequence(FMEnvelopeParameter param, int opSeqNum); + void addOperatorSequenceFMLoop(FMEnvelopeParameter param, int opSeqNum, const InstrumentSequenceLoop& loop); + void removeOperatorSequenceFMLoop(FMEnvelopeParameter param, int opSeqNum, int begin, int end); + void changeOperatorSequenceFMLoop(FMEnvelopeParameter param, int opSeqNum, int prevBegin, int prevEnd, const InstrumentSequenceLoop& loop); + void clearOperatorSequenceFMLoops(FMEnvelopeParameter param, int opSeqNum); + InstrumentSequenceLoopRoot getOperatorSequenceFMLoopRoot(FMEnvelopeParameter param, int opSeqNum) const; + void setOperatorSequenceFMRelease(FMEnvelopeParameter param, int opSeqNum, const InstrumentSequenceRelease& release); + InstrumentSequenceRelease getOperatorSequenceFMRelease(FMEnvelopeParameter param, int opSeqNum) const; + FMOperatorSequenceIter getOperatorSequenceFMIterator(FMEnvelopeParameter param, int opSeqNum) const; + std::multiset getOperatorSequenceFMUsers(FMEnvelopeParameter param, int opSeqNum) const; std::vector getOperatorSequenceFMEntriedIndices(FMEnvelopeParameter param) const; int findFirstAssignableOperatorSequenceFM(FMEnvelopeParameter param) const; @@ -125,16 +126,19 @@ int getInstrumentFMArpeggio(int instNum, FMOperatorType op); void setArpeggioFMType(int arpNum, SequenceType type); SequenceType getArpeggioFMType(int arpNum) const; - void addArpeggioFMSequenceCommand(int arpNum, int type, int data); - void removeArpeggioFMSequenceCommand(int arpNum); - void setArpeggioFMSequenceCommand(int arpNum, int cnt, int type, int data); - std::vector getArpeggioFMSequence(int arpNum); - void setArpeggioFMLoops(int arpNum, std::vector begins, std::vector ends, std::vector times); - std::vector getArpeggioFMLoops(int arpNum) const; - void setArpeggioFMRelease(int arpNum, ReleaseType type, int begin); - Release getArpeggioFMRelease(int arpNum) const; - std::unique_ptr getArpeggioFMIterator(int arpNum) const; - std::vector getArpeggioFMUsers(int arpNum) const; + void addArpeggioFMSequenceData(int arpNum, int data); + void removeArpeggioFMSequenceData(int arpNum); + void setArpeggioFMSequenceData(int arpNum, int cnt, int data); + std::vector getArpeggioFMSequence(int arpNum); + void addArpeggioFMLoop(int arpNum, const InstrumentSequenceLoop& loop); + void removeArpeggioFMLoop(int arpNum, int begin, int end); + void changeArpeggioFMLoop(int arpNum, int prevBegin, int prevEnd, const InstrumentSequenceLoop& loop); + void clearArpeggioFMLoops(int arpNum); + InstrumentSequenceLoopRoot getArpeggioFMLoopRoot(int arpNum) const; + void setArpeggioFMRelease(int arpNum, const InstrumentSequenceRelease& release); + InstrumentSequenceRelease getArpeggioFMRelease(int arpNum) const; + ArpeggioIter getArpeggioFMIterator(int arpNum) const; + std::multiset getArpeggioFMUsers(int arpNum) const; std::vector getArpeggioFMEntriedIndices() const; int findFirstAssignableArpeggioFM() const; @@ -144,16 +148,19 @@ int getInstrumentFMPitch(int instNum, FMOperatorType op); void setPitchFMType(int ptNum, SequenceType type); SequenceType getPitchFMType(int ptNum) const; - void addPitchFMSequenceCommand(int ptNum, int type, int data); - void removePitchFMSequenceCommand(int ptNum); - void setPitchFMSequenceCommand(int ptNum, int cnt, int type, int data); - std::vector getPitchFMSequence(int ptNum); - void setPitchFMLoops(int ptNum, std::vector begins, std::vector ends, std::vector times); - std::vector getPitchFMLoops(int ptNum) const; - void setPitchFMRelease(int ptNum, ReleaseType type, int begin); - Release getPitchFMRelease(int ptNum) const; - std::unique_ptr getPitchFMIterator(int ptNum) const; - std::vector getPitchFMUsers(int ptNum) const; + void addPitchFMSequenceData(int ptNum, int data); + void removePitchFMSequenceData(int ptNum); + void setPitchFMSequenceData(int ptNum, int cnt, int data); + std::vector getPitchFMSequence(int ptNum); + void addPitchFMLoop(int ptNum, const InstrumentSequenceLoop& loop); + void removePitchFMLoop(int ptNum, int begin, int end); + void changePitchFMLoop(int ptNum, int prevBegin, int prevEnd, const InstrumentSequenceLoop& loop); + void clearPitchFMLoops(int ptNum); + InstrumentSequenceLoopRoot getPitchFMLoopRoot(int ptNum) const; + void setPitchFMRelease(int ptNum, const InstrumentSequenceRelease& release); + InstrumentSequenceRelease getPitchFMRelease(int ptNum) const; + PitchIter getPitchFMIterator(int ptNum) const; + std::multiset getPitchFMUsers(int ptNum) const; std::vector getPitchFMEntriedIndices() const; int findFirstAssignablePitchFM() const; @@ -162,18 +169,9 @@ private: std::array, 128> envFM_; std::array, 128> lfoFM_; - std::unordered_map, 128>> opSeqFM_; - std::array, 128> arpFM_; - std::array, 128> ptFM_; - - static const FMEnvelopeParameter ENV_FM_PARAMS_[38]; - static const FMOperatorType FM_OP_TYPES_[5]; - - int cloneFMEnvelope(int srcNum); - int cloneFMLFO(int srcNum); - int cloneFMOperatorSequence(FMEnvelopeParameter param, int srcNum); - int cloneFMArpeggio(int srcNum); - int cloneFMPitch(int srcNum); + std::unordered_map>, 128>> opSeqFM_; + std::array>, 128> arpFM_; + std::array>, 128> ptFM_; bool equalPropertiesFM(std::shared_ptr a, std::shared_ptr b) const; @@ -183,16 +181,19 @@ bool getInstrumentSSGWaveformEnabled(int instNum) const; void setInstrumentSSGWaveform(int instNum, int wfNum); int getInstrumentSSGWaveform(int instNum); - void addWaveformSSGSequenceCommand(int wfNum, int type, int data); - void removeWaveformSSGSequenceCommand(int wfNum); - void setWaveformSSGSequenceCommand(int wfNum, int cnt, int type, int data); - std::vector getWaveformSSGSequence(int wfNum); - void setWaveformSSGLoops(int wfNum, std::vector begins, std::vector ends, std::vector times); - std::vector getWaveformSSGLoops(int wfNum) const; - void setWaveformSSGRelease(int wfNum, ReleaseType type, int begin); - Release getWaveformSSGRelease(int wfNum) const; - std::unique_ptr getWaveformSSGIterator(int wfNum) const; - std::vector getWaveformSSGUsers(int wfNum) const; + void addWaveformSSGSequenceData(int wfNum, const SSGWaveformUnit& data); + void removeWaveformSSGSequenceData(int wfNum); + void setWaveformSSGSequenceData(int wfNum, int cnt, const SSGWaveformUnit& data); + std::vector getWaveformSSGSequence(int wfNum); + void addWaveformSSGLoop(int wfNum, const InstrumentSequenceLoop& loop); + void removeWaveformSSGLoop(int wfNum, int begin, int end); + void changeWaveformSSGLoop(int wfNum, int prevBegin, int prevEnd, const InstrumentSequenceLoop& loop); + void clearWaveformSSGLoops(int wfNum); + InstrumentSequenceLoopRoot getWaveformSSGLoopRoot(int wfNum) const; + void setWaveformSSGRelease(int wfNum, const InstrumentSequenceRelease& release); + InstrumentSequenceRelease getWaveformSSGRelease(int wfNum) const; + SSGWaveformIter getWaveformSSGIterator(int wfNum) const; + std::multiset getWaveformSSGUsers(int wfNum) const; std::vector getWaveformSSGEntriedIndices() const; int findFirstAssignableWaveformSSG() const; @@ -200,16 +201,19 @@ bool getInstrumentSSGToneNoiseEnabled(int instNum) const; void setInstrumentSSGToneNoise(int instNum, int tnNum); int getInstrumentSSGToneNoise(int instNum); - void addToneNoiseSSGSequenceCommand(int tnNum, int type, int data); - void removeToneNoiseSSGSequenceCommand(int tnNum); - void setToneNoiseSSGSequenceCommand(int tnNum, int cnt, int type, int data); - std::vector getToneNoiseSSGSequence(int tnNum); - void setToneNoiseSSGLoops(int tnNum, std::vector begins, std::vector ends, std::vector times); - std::vector getToneNoiseSSGLoops(int tnNum) const; - void setToneNoiseSSGRelease(int tnNum, ReleaseType type, int begin); - Release getToneNoiseSSGRelease(int tnNum) const; - std::unique_ptr getToneNoiseSSGIterator(int tnNum) const; - std::vector getToneNoiseSSGUsers(int tnNum) const; + void addToneNoiseSSGSequenceData(int tnNum, int data); + void removeToneNoiseSSGSequenceData(int tnNum); + void setToneNoiseSSGSequenceData(int tnNum, int cnt, int data); + std::vector getToneNoiseSSGSequence(int tnNum); + void addToneNoiseSSGLoop(int tnNum, const InstrumentSequenceLoop& loop); + void removeToneNoiseSSGLoop(int tnNum, int begin, int end); + void changeToneNoiseSSGLoop(int tnNum, int prevBegin, int prevEnd, const InstrumentSequenceLoop& loop); + void clearToneNoiseSSGLoops(int tnNum); + InstrumentSequenceLoopRoot getToneNoiseSSGLoopRoot(int tnNum) const; + void setToneNoiseSSGRelease(int tnNum, const InstrumentSequenceRelease& release); + InstrumentSequenceRelease getToneNoiseSSGRelease(int tnNum) const; + SSGToneNoiseIter getToneNoiseSSGIterator(int tnNum) const; + std::multiset getToneNoiseSSGUsers(int tnNum) const; std::vector getToneNoiseSSGEntriedIndices() const; int findFirstAssignableToneNoiseSSG() const; @@ -217,16 +221,19 @@ bool getInstrumentSSGEnvelopeEnabled(int instNum) const; void setInstrumentSSGEnvelope(int instNum, int envNum); int getInstrumentSSGEnvelope(int instNum); - void addEnvelopeSSGSequenceCommand(int envNum, int type, int data); - void removeEnvelopeSSGSequenceCommand(int envNum); - void setEnvelopeSSGSequenceCommand(int envNum, int cnt, int type, int data); - std::vector getEnvelopeSSGSequence(int envNum); - void setEnvelopeSSGLoops(int envNum, std::vector begins, std::vector ends, std::vector times); - std::vector getEnvelopeSSGLoops(int envNum) const; - void setEnvelopeSSGRelease(int envNum, ReleaseType type, int begin); - Release getEnvelopeSSGRelease(int envNum) const; - std::unique_ptr getEnvelopeSSGIterator(int envNum) const; - std::vector getEnvelopeSSGUsers(int envNum) const; + void addEnvelopeSSGSequenceData(int envNum, const SSGEnvelopeUnit& data); + void removeEnvelopeSSGSequenceData(int envNum); + void setEnvelopeSSGSequenceData(int envNum, int cnt, const SSGEnvelopeUnit& data); + std::vector getEnvelopeSSGSequence(int envNum); + void addEnvelopeSSGLoop(int envNum, const InstrumentSequenceLoop& loop); + void removeEnvelopeSSGLoop(int envNum, int begin, int end); + void changeEnvelopeSSGLoop(int envNum, int prevBegin, int prevEnd, const InstrumentSequenceLoop& loop); + void clearEnvelopeSSGLoops(int envNum); + InstrumentSequenceLoopRoot getEnvelopeSSGLoopRoot(int envNum) const; + void setEnvelopeSSGRelease(int envNum, const InstrumentSequenceRelease& release); + InstrumentSequenceRelease getEnvelopeSSGRelease(int envNum) const; + SSGEnvelopeIter getEnvelopeSSGIterator(int envNum) const; + std::multiset getEnvelopeSSGUsers(int envNum) const; std::vector getEnvelopeSSGEntriedIndices() const; int findFirstAssignableEnvelopeSSG() const; @@ -236,16 +243,19 @@ int getInstrumentSSGArpeggio(int instNum); void setArpeggioSSGType(int arpNum, SequenceType type); SequenceType getArpeggioSSGType(int arpNum) const; - void addArpeggioSSGSequenceCommand(int arpNum, int type, int data); - void removeArpeggioSSGSequenceCommand(int arpNum); - void setArpeggioSSGSequenceCommand(int arpNum, int cnt, int type, int data); - std::vector getArpeggioSSGSequence(int arpNum); - void setArpeggioSSGLoops(int arpNum, std::vector begins, std::vector ends, std::vector times); - std::vector getArpeggioSSGLoops(int arpNum) const; - void setArpeggioSSGRelease(int arpNum, ReleaseType type, int begin); - Release getArpeggioSSGRelease(int arpNum) const; - std::unique_ptr getArpeggioSSGIterator(int arpNum) const; - std::vector getArpeggioSSGUsers(int arpNum) const; + void addArpeggioSSGSequenceData(int arpNum, int data); + void removeArpeggioSSGSequenceData(int arpNum); + void setArpeggioSSGSequenceData(int arpNum, int cnt, int data); + std::vector getArpeggioSSGSequence(int arpNum); + void addArpeggioSSGLoop(int arpNum, const InstrumentSequenceLoop& loop); + void removeArpeggioSSGLoop(int arpNum, int begin, int end); + void changeArpeggioSSGLoop(int arpNum, int prevBegin, int prevEnd, const InstrumentSequenceLoop& loop); + void clearArpeggioSSGLoops(int arpNum); + InstrumentSequenceLoopRoot getArpeggioSSGLoopRoot(int arpNum) const; + void setArpeggioSSGRelease(int arpNum, const InstrumentSequenceRelease& release); + InstrumentSequenceRelease getArpeggioSSGRelease(int arpNum) const; + ArpeggioIter getArpeggioSSGIterator(int arpNum) const; + std::multiset getArpeggioSSGUsers(int arpNum) const; std::vector getArpeggioSSGEntriedIndices() const; int findFirstAssignableArpeggioSSG() const; @@ -255,31 +265,28 @@ int getInstrumentSSGPitch(int instNum); void setPitchSSGType(int ptNum, SequenceType type); SequenceType getPitchSSGType(int ptNum) const; - void addPitchSSGSequenceCommand(int ptNum, int type, int data); - void removePitchSSGSequenceCommand(int ptNum); - void setPitchSSGSequenceCommand(int ptNum, int cnt, int type, int data); - std::vector getPitchSSGSequence(int ptNum); - void setPitchSSGLoops(int ptNum, std::vector begins, std::vector ends, std::vector times); - std::vector getPitchSSGLoops(int ptNum) const; - void setPitchSSGRelease(int ptNum, ReleaseType type, int begin); - Release getPitchSSGRelease(int ptNum) const; - std::unique_ptr getPitchSSGIterator(int ptNum) const; - std::vector getPitchSSGUsers(int ptNum) const; + void addPitchSSGSequenceData(int ptNum, int data); + void removePitchSSGSequenceData(int ptNum); + void setPitchSSGSequenceData(int ptNum, int cnt, int data); + std::vector getPitchSSGSequence(int ptNum); + void addPitchSSGLoop(int ptNum, const InstrumentSequenceLoop& loop); + void removePitchSSGLoop(int ptNum, int begin, int end); + void changePitchSSGLoop(int ptNum, int prevBegin, int prevEnd, const InstrumentSequenceLoop& loop); + void clearPitchSSGLoops(int ptNum); + InstrumentSequenceLoopRoot getPitchSSGLoopRoot(int ptNum) const; + void setPitchSSGRelease(int ptNum, const InstrumentSequenceRelease& release); + InstrumentSequenceRelease getPitchSSGRelease(int ptNum) const; + PitchIter getPitchSSGIterator(int ptNum) const; + std::multiset getPitchSSGUsers(int ptNum) const; std::vector getPitchSSGEntriedIndices() const; int findFirstAssignablePitchSSG() const; private: - std::array, 128> wfSSG_; - std::array, 128> envSSG_; - std::array, 128> tnSSG_; - std::array, 128> arpSSG_; - std::array, 128> ptSSG_; - - int cloneSSGWaveform(int srcNum); - int cloneSSGToneNoise(int srcNum); - int cloneSSGEnvelope(int srcNum); - int cloneSSGArpeggio(int srcNum); - int cloneSSGPitch(int srcNum); + std::array>, 128> wfSSG_; + std::array>, 128> envSSG_; + std::array>, 128> tnSSG_; + std::array>, 128> arpSSG_; + std::array>, 128> ptSSG_; bool equalPropertiesSSG(std::shared_ptr a, std::shared_ptr b) const; @@ -293,14 +300,15 @@ int getSampleADPCMRootDeltaN(int sampNum) const; void setSampleADPCMRepeatEnabled(int sampNum, bool enabled); bool isSampleADPCMRepeatable(int sampNum) const; - void storeSampleADPCMRawSample(int sampNum, std::vector sample); + void storeSampleADPCMRawSample(int sampNum, const std::vector& sample); + void storeSampleADPCMRawSample(int sampNum, std::vector&& sample); void clearSampleADPCMRawSample(int sampNum); std::vector getSampleADPCMRawSample(int sampNum) const; void setSampleADPCMStartAddress(int sampNum, size_t addr); size_t getSampleADPCMStartAddress(int sampNum) const; void setSampleADPCMStopAddress(int sampNum, size_t addr); size_t getSampleADPCMStopAddress(int sampNum) const; - std::vector getSampleADPCMUsers(int sampNum) const; + std::multiset getSampleADPCMUsers(int sampNum) const; std::vector getSampleADPCMEntriedIndices() const; std::vector getSampleADPCMValidIndices() const; void clearUnusedSamplesADPCM(); @@ -310,16 +318,19 @@ bool getInstrumentADPCMEnvelopeEnabled(int instNum) const; void setInstrumentADPCMEnvelope(int instNum, int envNum); int getInstrumentADPCMEnvelope(int instNum); - void addEnvelopeADPCMSequenceCommand(int envNum, int type, int data); - void removeEnvelopeADPCMSequenceCommand(int envNum); - void setEnvelopeADPCMSequenceCommand(int envNum, int cnt, int type, int data); - std::vector getEnvelopeADPCMSequence(int envNum); - void setEnvelopeADPCMLoops(int envNum, std::vector begins, std::vector ends, std::vector times); - std::vector getEnvelopeADPCMLoops(int envNum) const; - void setEnvelopeADPCMRelease(int envNum, ReleaseType type, int begin); - Release getEnvelopeADPCMRelease(int envNum) const; - std::unique_ptr getEnvelopeADPCMIterator(int envNum) const; - std::vector getEnvelopeADPCMUsers(int envNum) const; + void addEnvelopeADPCMSequenceData(int envNum, int data); + void removeEnvelopeADPCMSequenceData(int envNum); + void setEnvelopeADPCMSequenceData(int envNum, int cnt, int data); + std::vector getEnvelopeADPCMSequence(int envNum); + void addEnvelopeADPCMLoop(int envNum, const InstrumentSequenceLoop& loop); + void removeEnvelopeADPCMLoop(int envNum, int begin, int end); + void changeEnvelopeADPCMLoop(int envNum, int prevBegin, int prevEnd, const InstrumentSequenceLoop& loop); + void clearEnvelopeADPCMLoops(int envNum); + InstrumentSequenceLoopRoot getEnvelopeADPCMLoopRoot(int envNum) const; + void setEnvelopeADPCMRelease(int envNum, const InstrumentSequenceRelease& release); + InstrumentSequenceRelease getEnvelopeADPCMRelease(int envNum) const; + ADPCMEnvelopeIter getEnvelopeADPCMIterator(int envNum) const; + std::multiset getEnvelopeADPCMUsers(int envNum) const; std::vector getEnvelopeADPCMEntriedIndices() const; int findFirstAssignableEnvelopeADPCM() const; @@ -329,16 +340,19 @@ int getInstrumentADPCMArpeggio(int instNum); void setArpeggioADPCMType(int arpNum, SequenceType type); SequenceType getArpeggioADPCMType(int arpNum) const; - void addArpeggioADPCMSequenceCommand(int arpNum, int type, int data); - void removeArpeggioADPCMSequenceCommand(int arpNum); - void setArpeggioADPCMSequenceCommand(int arpNum, int cnt, int type, int data); - std::vector getArpeggioADPCMSequence(int arpNum); - void setArpeggioADPCMLoops(int arpNum, std::vector begins, std::vector ends, std::vector times); - std::vector getArpeggioADPCMLoops(int arpNum) const; - void setArpeggioADPCMRelease(int arpNum, ReleaseType type, int begin); - Release getArpeggioADPCMRelease(int arpNum) const; - std::unique_ptr getArpeggioADPCMIterator(int arpNum) const; - std::vector getArpeggioADPCMUsers(int arpNum) const; + void addArpeggioADPCMSequenceData(int arpNum, int data); + void removeArpeggioADPCMSequenceData(int arpNum); + void setArpeggioADPCMSequenceData(int arpNum, int cnt, int data); + std::vector getArpeggioADPCMSequence(int arpNum); + void addArpeggioADPCMLoop(int arpNum, const InstrumentSequenceLoop& loop); + void removeArpeggioADPCMLoop(int arpNum, int begin, int end); + void changeArpeggioADPCMLoop(int arpNum, int prevBegin, int prevEnd, const InstrumentSequenceLoop& loop); + void clearArpeggioADPCMLoops(int arpNum); + InstrumentSequenceLoopRoot getArpeggioADPCMLoopRoot(int arpNum) const; + void setArpeggioADPCMRelease(int arpNum, const InstrumentSequenceRelease& release); + InstrumentSequenceRelease getArpeggioADPCMRelease(int arpNum) const; + ArpeggioIter getArpeggioADPCMIterator(int arpNum) const; + std::multiset getArpeggioADPCMUsers(int arpNum) const; std::vector getArpeggioADPCMEntriedIndices() const; int findFirstAssignableArpeggioADPCM() const; @@ -348,29 +362,27 @@ int getInstrumentADPCMPitch(int instNum); void setPitchADPCMType(int ptNum, SequenceType type); SequenceType getPitchADPCMType(int ptNum) const; - void addPitchADPCMSequenceCommand(int ptNum, int type, int data); - void removePitchADPCMSequenceCommand(int ptNum); - void setPitchADPCMSequenceCommand(int ptNum, int cnt, int type, int data); - std::vector getPitchADPCMSequence(int ptNum); - void setPitchADPCMLoops(int ptNum, std::vector begins, std::vector ends, std::vector times); - std::vector getPitchADPCMLoops(int ptNum) const; - void setPitchADPCMRelease(int ptNum, ReleaseType type, int begin); - Release getPitchADPCMRelease(int ptNum) const; - std::unique_ptr getPitchADPCMIterator(int ptNum) const; - std::vector getPitchADPCMUsers(int ptNum) const; + void addPitchADPCMSequenceData(int ptNum, int data); + void removePitchADPCMSequenceData(int ptNum); + void setPitchADPCMSequenceData(int ptNum, int cnt, int data); + std::vector getPitchADPCMSequence(int ptNum); + void addPitchADPCMLoop(int ptNum, const InstrumentSequenceLoop& loop); + void removePitchADPCMLoop(int ptNum, int begin, int end); + void changePitchADPCMLoop(int ptNum, int prevBegin, int prevEnd, const InstrumentSequenceLoop& loop); + void clearPitchADPCMLoops(int ptNum); + InstrumentSequenceLoopRoot getPitchADPCMLoopRoot(int ptNum) const; + void setPitchADPCMRelease(int ptNum, const InstrumentSequenceRelease& release); + InstrumentSequenceRelease getPitchADPCMRelease(int ptNum) const; + PitchIter getPitchADPCMIterator(int ptNum) const; + std::multiset getPitchADPCMUsers(int ptNum) const; std::vector getPitchADPCMEntriedIndices() const; int findFirstAssignablePitchADPCM() const; private: std::array, 128> sampADPCM_; - std::array, 128> envADPCM_; - std::array, 128> arpADPCM_; - std::array, 128> ptADPCM_; - - int cloneADPCMSample(int srcNum); - int cloneADPCMEnvelope(int srcNum); - int cloneADPCMArpeggio(int srcNum); - int cloneADPCMPitch(int srcNum); + std::array>, 128> envADPCM_; + std::array>, 128> arpADPCM_; + std::array>, 128> ptADPCM_; bool equalPropertiesADPCM(std::shared_ptr a, std::shared_ptr b) const; diff -Nru bambootracker-0.4.5+git20121209/BambooTracker/instrument/lfo_fm.cpp bambootracker-0.4.6/BambooTracker/instrument/lfo_fm.cpp --- bambootracker-0.4.5+git20121209/BambooTracker/instrument/lfo_fm.cpp 2020-12-08 14:05:28.000000000 +0000 +++ bambootracker-0.4.6/BambooTracker/instrument/lfo_fm.cpp 2021-02-11 14:37:17.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (C) 2018-2020 Rerrah + * Copyright (C) 2018-2021 Rerrah * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation @@ -24,9 +24,20 @@ */ #include "lfo_fm.hpp" -#include -constexpr int LFOFM::DEF_AM_OP_[4]; +namespace +{ +const std::unordered_map DEF_PARAMS = { + { FMLFOParameter::FREQ, 0 }, + { FMLFOParameter::PMS, 0 }, + { FMLFOParameter::AMS, 0 }, + { FMLFOParameter::AM1, 0 }, + { FMLFOParameter::AM2, 0 }, + { FMLFOParameter::AM3, 0 }, + { FMLFOParameter::AM4, 0 }, + { FMLFOParameter::Count, 0 } +}; +} LFOFM::LFOFM(int n) : AbstractInstrumentProperty (n) @@ -34,24 +45,6 @@ clearParameters(); } -LFOFM::LFOFM(const LFOFM& other) - : AbstractInstrumentProperty (other) -{ - freq_ = other.freq_; - ams_ = other.ams_; - pms_ = other.pms_; - cnt_ = other.cnt_; - for (int i = 0; i < 4; ++i) - amOp_[i] = other.amOp_[i]; -} - -bool operator==(const LFOFM& a, const LFOFM& b) -{ - return (a.freq_ == b.freq_ && a.ams_ == b.ams_ && a.pms_ == b.pms_ && a.cnt_ == b.cnt_ - && a.amOp_[0] == b.amOp_[0] && a.amOp_[1] == b.amOp_[1] - && a.amOp_[2] == b.amOp_[2] && a.amOp_[3] == b.amOp_[3]); -} - std::unique_ptr LFOFM::clone() { std::unique_ptr clone = std::make_unique(*this); @@ -61,53 +54,20 @@ void LFOFM::setParameterValue(FMLFOParameter param, int value) { - switch (param) { - case FMLFOParameter::FREQ: freq_ = value; break; - case FMLFOParameter::PMS: pms_ = value; break; - case FMLFOParameter::AMS: ams_ = value; break; - case FMLFOParameter::Count: cnt_ = value; break; - case FMLFOParameter::AM1: amOp_[0] = value; break; - case FMLFOParameter::AM2: amOp_[1] = value; break; - case FMLFOParameter::AM3: amOp_[2] = value; break; - case FMLFOParameter::AM4: amOp_[3] = value; break; - } + params_.at(param) = value; } int LFOFM::getParameterValue(FMLFOParameter param) const { - switch (param) { - case FMLFOParameter::FREQ: return freq_; - case FMLFOParameter::PMS: return pms_; - case FMLFOParameter::AMS: return ams_; - case FMLFOParameter::Count: return cnt_; - case FMLFOParameter::AM1: return amOp_[0]; - case FMLFOParameter::AM2: return amOp_[1]; - case FMLFOParameter::AM3: return amOp_[2]; - case FMLFOParameter::AM4: return amOp_[3]; - default: throw std::invalid_argument("Unexpected FMLFOParameter."); - } + return params_.at(param); } bool LFOFM::isEdited() const { - if (freq_ != DEF_FREQ_ - || pms_ != DEF_PMS_ - || ams_ != DEF_AMS_ - || cnt_ != DEF_CNT_) - return true; - for (int i = 0; i < 4; ++i) { - if (amOp_[i] != DEF_AM_OP_[i]) return true; - } - return false; + return params_ != DEF_PARAMS; } void LFOFM::clearParameters() { - freq_ = DEF_FREQ_; - pms_ = DEF_PMS_; - ams_ = DEF_AMS_; - cnt_ = DEF_CNT_; - for (int i = 0; i < 4; ++i) { - amOp_[i] = DEF_AM_OP_[i]; - } + params_ = DEF_PARAMS; } diff -Nru bambootracker-0.4.5+git20121209/BambooTracker/instrument/lfo_fm.hpp bambootracker-0.4.6/BambooTracker/instrument/lfo_fm.hpp --- bambootracker-0.4.5+git20121209/BambooTracker/instrument/lfo_fm.hpp 2020-12-08 14:05:28.000000000 +0000 +++ bambootracker-0.4.6/BambooTracker/instrument/lfo_fm.hpp 2021-02-11 14:37:17.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (C) 2018-2020 Rerrah + * Copyright (C) 2018-2021 Rerrah * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation @@ -25,19 +25,23 @@ #pragma once +#include #include #include "abstract_instrument_property.hpp" +#include "enum_hash.hpp" -enum class FMLFOParameter; - +enum class FMLFOParameter +{ + FREQ, AMS, PMS, Count, + AM1, AM2, AM3, AM4 +}; -class LFOFM : public AbstractInstrumentProperty +class LFOFM final : public AbstractInstrumentProperty { public: explicit LFOFM(int n); - LFOFM(const LFOFM& other); - friend bool operator==(const LFOFM& a, const LFOFM& b); + friend bool operator==(const LFOFM& a, const LFOFM& b) { return a.params_ == b.params_; } friend bool operator!=(const LFOFM& a, const LFOFM& b) { return !(a == b); } std::unique_ptr clone(); @@ -49,21 +53,5 @@ void clearParameters() override; private: - int freq_; - int pms_; - int ams_; - int amOp_[4]; - int cnt_; - - static constexpr int DEF_FREQ_ = 0; - static constexpr int DEF_PMS_ = 0; - static constexpr int DEF_AMS_ = 0; - static constexpr int DEF_AM_OP_[4] = { 0, 0, 0, 0 }; - static constexpr int DEF_CNT_ = 0; -}; - -enum class FMLFOParameter -{ - FREQ, AMS, PMS, Count, - AM1, AM2, AM3, AM4 + std::unordered_map params_; }; diff -Nru bambootracker-0.4.5+git20121209/BambooTracker/instrument/sample_adpcm.cpp bambootracker-0.4.6/BambooTracker/instrument/sample_adpcm.cpp --- bambootracker-0.4.5+git20121209/BambooTracker/instrument/sample_adpcm.cpp 1970-01-01 00:00:00.000000000 +0000 +++ bambootracker-0.4.6/BambooTracker/instrument/sample_adpcm.cpp 2021-02-11 14:37:17.000000000 +0000 @@ -0,0 +1,76 @@ +/* + * Copyright (C) 2020-2021 Rerrah + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +#include "sample_adpcm.hpp" + +namespace +{ +constexpr int DEF_RT_DELTAN_ = 0x49cd; // 16000Hz +constexpr bool DEF_REPET_ = false; +} + +SampleADPCM::SampleADPCM(int num) + : AbstractInstrumentProperty (num) +{ + clearParameters(); +} + +bool operator==(const SampleADPCM& a, const SampleADPCM& b) { + return (a.rootKeyNum_ == b.rootKeyNum_ && a.rootDeltaN_ == b.rootDeltaN_ + && a.isRepeated_ == b.isRepeated_ && a.sample_ == b.sample_); +} + +std::unique_ptr SampleADPCM::clone() +{ + std::unique_ptr clone = std::make_unique(*this); + clone->clearUserInstruments(); + return clone; +} + +void SampleADPCM::clearSample() +{ + startAddress_ = 0; + stopAddress_ = 0; + sample_ = std::vector(1); +} + +bool SampleADPCM::isEdited() const +{ + if (rootKeyNum_ != DEF_ROOT_KEY + || rootDeltaN_ != DEF_RT_DELTAN_ + || isRepeated_ != DEF_REPET_ + || sample_.size() != 1 + || sample_.front() != 0) + return true; + return false; +} + +void SampleADPCM::clearParameters() +{ + rootKeyNum_ = DEF_ROOT_KEY; + rootDeltaN_ = DEF_RT_DELTAN_; + isRepeated_ = DEF_REPET_; + clearSample(); +} diff -Nru bambootracker-0.4.5+git20121209/BambooTracker/instrument/sample_adpcm.hpp bambootracker-0.4.6/BambooTracker/instrument/sample_adpcm.hpp --- bambootracker-0.4.5+git20121209/BambooTracker/instrument/sample_adpcm.hpp 1970-01-01 00:00:00.000000000 +0000 +++ bambootracker-0.4.6/BambooTracker/instrument/sample_adpcm.hpp 2021-02-11 14:37:17.000000000 +0000 @@ -0,0 +1,74 @@ +/* + * Copyright (C) 2020-2021 Rerrah + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +#pragma once + +#include +#include +#include +#include +#include "abstract_instrument_property.hpp" + +class SampleADPCM final : public AbstractInstrumentProperty +{ +public: + explicit SampleADPCM(int num); + + friend bool operator==(const SampleADPCM& a, const SampleADPCM& b); + friend bool operator!=(const SampleADPCM& a, const SampleADPCM& b) { return !(a == b); } + + std::unique_ptr clone(); + + inline void setRootKeyNumber(int n) noexcept { rootKeyNum_ = n; } + inline int getRootKeyNumber() const noexcept {return rootKeyNum_; } + inline void setRootDeltaN(int dn) noexcept { rootDeltaN_ = dn; } + inline int getRootDeltaN() const noexcept { return rootDeltaN_; } + inline void setRepeatEnabled(bool enabled) noexcept { isRepeated_ = enabled; } + inline bool isRepeatable() const noexcept { return isRepeated_; } + inline void storeSample(const std::vector& sample) { sample_ = sample; } + inline void storeSample(std::vector&& sample) { sample_ = std::move(sample); } + inline std::vector getSamples() const { return sample_; } + void clearSample(); + inline void setStartAddress(size_t addr) noexcept { startAddress_ = addr; } + inline size_t getStartAddress() const noexcept { return startAddress_; } + inline void setStopAddress(size_t addr) noexcept { stopAddress_ = addr; } + inline size_t getStopAddress() const noexcept { return stopAddress_; } + + bool isEdited() const override; + void clearParameters() override; + + static constexpr int DEF_ROOT_KEY = 60; // C5 + + inline static int calculateADPCMDeltaN(unsigned int rate) + { + return static_cast(std::round((rate << 16) / 55500.)); + } + +private: + int rootKeyNum_, rootDeltaN_; + bool isRepeated_; + std::vector sample_; + size_t startAddress_, stopAddress_; +}; diff -Nru bambootracker-0.4.5+git20121209/BambooTracker/instrument/sequence_iterator_interface.hpp bambootracker-0.4.6/BambooTracker/instrument/sequence_iterator_interface.hpp --- bambootracker-0.4.5+git20121209/BambooTracker/instrument/sequence_iterator_interface.hpp 2020-12-08 14:05:28.000000000 +0000 +++ bambootracker-0.4.6/BambooTracker/instrument/sequence_iterator_interface.hpp 2021-02-11 14:37:17.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (C) 2018-2019 Rerrah + * Copyright (C) 2018-2021 Rerrah * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation @@ -25,20 +25,33 @@ #pragma once +enum class SequenceType +{ + PlainSequence, + AbsoluteSequence, + FixedSequence, + RelativeSequence +}; + +template class SequenceIteratorInterface { public: virtual ~SequenceIteratorInterface() = default; - /// -1: sequence end - /// else: position in the sequence - virtual int getPosition() const = 0; - /// 0: absolute - /// 1: fixed - /// 2: relative - virtual int getSequenceType() const = 0; - virtual int getCommandType() const = 0; - virtual int getCommandData() const = 0; - virtual int next(bool isReleaseBegin = false) = 0; + + static constexpr int END_SEQ_POS = -1; + inline int pos() const noexcept { return pos_; } + inline bool hasEnded() const noexcept { return pos_ == END_SEQ_POS; } + + virtual SequenceType type() const = 0; + virtual T data() const = 0; + + virtual int next() = 0; virtual int front() = 0; + virtual int release() = 0; virtual int end() = 0; + +protected: + explicit SequenceIteratorInterface(int initPos = 0) : pos_(initPos) {} + int pos_; }; diff -Nru bambootracker-0.4.5+git20121209/BambooTracker/instrument/sequence_property.cpp bambootracker-0.4.6/BambooTracker/instrument/sequence_property.cpp --- bambootracker-0.4.5+git20121209/BambooTracker/instrument/sequence_property.cpp 1970-01-01 00:00:00.000000000 +0000 +++ bambootracker-0.4.6/BambooTracker/instrument/sequence_property.cpp 2021-02-11 14:37:17.000000000 +0000 @@ -0,0 +1,318 @@ +/* + * Copyright (C) 2018-2021 Rerrah + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +#include "sequence_property.hpp" +#include "utils.hpp" + +InstrumentSequenceBaseUnit::InstrumentSequenceBaseUnit() noexcept + : data(ERR_DATA) +{ +} + +InstrumentSequenceBaseUnit::InstrumentSequenceBaseUnit(int d) noexcept + : data(d) +{ +} + +InstrumentSequenceExtendUnit::InstrumentSequenceExtendUnit() noexcept + : InstrumentSequenceBaseUnit(), + type(InstrumentSequenceExtendUnit::UnusedSubdata), + subdata(InstrumentSequenceBaseUnit::ERR_DATA) +{ +} + +InstrumentSequenceExtendUnit::InstrumentSequenceExtendUnit(int d, SubdataType subType, int subData) noexcept + : InstrumentSequenceBaseUnit(d), type(subType), subdata(subData) +{ +} + +InstrumentSequenceExtendUnit InstrumentSequenceExtendUnit::makeOnlyDataUnit(int data) noexcept +{ + return InstrumentSequenceExtendUnit(data, SubdataType::UnusedSubdata, ERR_DATA); +} + +InstrumentSequenceExtendUnit InstrumentSequenceExtendUnit::makeRawUnit(int data, int sub) noexcept +{ + return InstrumentSequenceExtendUnit(data, SubdataType::RawSubdata, sub); +} + +InstrumentSequenceExtendUnit InstrumentSequenceExtendUnit::makeRatioUnit(int data, int subFirst, int subSecond) noexcept +{ + return InstrumentSequenceExtendUnit(data, SubdataType::RatioSubdata, ((1 << 16) | (subFirst << 8) | subSecond)); +} + +InstrumentSequenceExtendUnit InstrumentSequenceExtendUnit::makeShiftUnit(int data, int rshift) noexcept +{ + int sub = (rshift > 0) ? ((2 << 16) | rshift) : ((3 << 16) | -rshift); + return InstrumentSequenceExtendUnit(data, SubdataType::ShiftSubdata, sub); +} + +InstrumentSequenceExtendUnit InstrumentSequenceExtendUnit::makeUnitWithDecode(int data, int subsrc) noexcept +{ + SubdataType type; + if (subsrc & 0x20000) type = SubdataType::ShiftSubdata; + else type = (subsrc & 0x10000) ? SubdataType::RatioSubdata : SubdataType::RawSubdata; + return InstrumentSequenceExtendUnit(data, type, subsrc); +} + +void InstrumentSequenceExtendUnit::getSubdataAsRaw(int& raw) const noexcept +{ + raw = subdata; +} + +void InstrumentSequenceExtendUnit::getSubdataAsRatio(int& first, int& second) const noexcept +{ + first = (subdata & 0x0ff00) >> 8; + second = subdata & 0x000ff; +} + +void InstrumentSequenceExtendUnit::getSubdataAsShift(int& rshift) const noexcept +{ + rshift = (subdata & 0x10000) ? -(0x0ffff & data) : (0x0ffff & data); +} + +namespace +{ +inline InstrumentSequenceLoop::Ptr makeSequenceLoopPtr(const InstrumentSequenceLoop& loop) +{ + return std::make_shared(loop); +} +} + +InstrumentSequenceLoop::InstrumentSequenceLoop(int begin, int end, int times) + : begin_(begin), end_(end), times_(times) +{ +} + +void InstrumentSequenceLoop::setBeginPos(int pos) +{ + if (begin_ < pos && hasInnerLoop()) { + auto&& it = utils::findIf(childs_, [pos](const auto& pair) { + return pos <= pair.second->end_; + }); + if (it == childs_.end()) { + removeAllInnerLoops(); + } + else { + if (it->second->begin_ < pos) it->second->setBeginPos(pos); + childs_.erase(childs_.begin(), it); + } + } + begin_ = pos; +} + +void InstrumentSequenceLoop::setEndPos(int pos) +{ + if (pos < end_ && hasInnerLoop()) { + auto&& it = std::find_if(childs_.rbegin(), childs_.rend(), [pos](const auto& pair) { + return pair.second->begin_ <= pos; + }); + if (it == childs_.rend()) { + removeAllInnerLoops(); + } + else { + if (pos < it->second->end_) it->second->setEndPos(pos); + childs_.erase(it.base(), childs_.end()); + } + } + end_ = pos; +} + +bool InstrumentSequenceLoop::addInnerLoop(const InstrumentSequenceLoop& inner) +{ + for (auto& pair : childs_) { + Ptr& loop = pair.second; + if (loop->isOverlapped(inner)) { + if (loop->hasSameRegion(inner)) { + loop->times_ = inner.times_; + return true; + } + else if (loop->isContainable(inner)) { + return loop->addInnerLoop(inner); + } + else { // Illegal region + return false; + } + } + else if (loop->begin_ < inner.begin_) break; + } + + // Add new region + childs_.insert(std::make_pair(inner.begin_, makeSequenceLoopPtr(inner))); + return true; +} + +bool InstrumentSequenceLoop::changeInnerLoop(int prevBegin, int prevEnd, const InstrumentSequenceLoop& loop) +{ + if (hasInnerLoopBeginAt(prevBegin)) { + InstrumentSequenceLoop::Ptr lpt = getInnerLoopBeginAt(prevBegin); + if (lpt->end_ != prevEnd) return lpt->changeInnerLoop(prevBegin, prevEnd, loop); + if (prevBegin != loop.begin_) lpt->setBeginPos(loop.begin_); + if (prevEnd != loop.end_) lpt->setEndPos(loop.end_); + if (lpt->times_ != loop.times_) lpt->setTimes(loop.times_); + return true; + } + else { + return false; + } +} + +void InstrumentSequenceLoop::removeInnerLoop(int begin, int end) +{ + for (auto& pair : childs_) { + Ptr& loop = pair.second; + if (loop->isOverlapped(begin, end)) { + if (loop->hasSameRegion(begin, end)) { + childs_.erase(pair.first); + } + else if (loop->isContainable(begin, end)) { + loop->removeInnerLoop(begin, end); + } + return; + } + else if (loop->begin_ < begin) return; + } +} + +void InstrumentSequenceLoop::removeAllInnerLoops() +{ + childs_.clear(); +} + +std::vector InstrumentSequenceLoop::getAllInnerLoops() const +{ + std::vector list; + for (const auto& pair : childs_) { + list.push_back(*pair.second); + auto in = pair.second->getAllInnerLoops(); + list.insert(list.end(), in.begin(), in.end()); + } + return list; +} + +bool InstrumentSequenceLoop::isOverlapped(int begin, int end) const +{ + if (begin_ == begin) return true; + if (begin_ < begin) return (begin <= end_); + else /* begin < begin_ */ return (begin_ <= end); +} + +bool InstrumentSequenceLoop::isContainable(int begin, int end) const +{ + return ((begin_ < begin && end <= end_) + || (begin_ == begin && end < end_)); +} + +bool InstrumentSequenceLoop::hasSameRegion(int begin, int end) const +{ + return (begin_ == begin && end_ == end); +} + +InstrumentSequenceLoop InstrumentSequenceLoop::clone() const +{ + InstrumentSequenceLoop l(begin_, end_, times_); + for (const auto& pair : childs_) { + l.childs_.insert(std::make_pair(pair.first, makeSequenceLoopPtr(pair.second->clone()))); + } + return l; +} + +InstrumentSequenceLoopRoot InstrumentSequenceLoopRoot::clone() const +{ + InstrumentSequenceLoopRoot r(end_); + for (const auto& pair : childs_) { + r.childs_.insert(std::make_pair(pair.first, makeSequenceLoopPtr(pair.second->clone()))); + } + return r; +} + +namespace inst_utils +{ +LoopStack::StackItem::StackItem(const InstrumentSequenceLoop::Ptr& ptr) + : loop(ptr), count(ptr->getTimes()), isInfinite(ptr->isInfinite()) +{ +} + +LoopStack::LoopStack(const std::shared_ptr& ptr) + : stack_{ StackItem(std::static_pointer_cast(ptr)) } +{ +} + +void LoopStack::clear() +{ + stack_.erase(stack_.begin() + 1, stack_.end()); +} + +void LoopStack::pushLoopsAtPos(int pos) +{ + while (true) { + InstrumentSequenceLoop::Ptr& loop = stack_.back().loop; + if (!loop->hasInnerLoopBeginAt(pos)) break; + stack_.emplace_back(loop->getInnerLoopBeginAt(pos)); + } +} + +int LoopStack::checkLoopEndAndNextPos(int curPos) +{ + while (stack_.size() > 1) { + StackItem& item = stack_.back(); + if (item.loop->getEndPos() != curPos) { + return curPos + 1; + } + if (item.isInfinite) { + return item.loop->getBeginPos(); + } + if (--(item.count)) { + return item.loop->getBeginPos(); + } + else { + stack_.pop_back(); + } + } + return curPos + 1; +} +} + +InstrumentSequenceRelease::InstrumentSequenceRelease(ReleaseType type, int beginPos) + : type_(type), + begin_(type_ == ReleaseType::NoRelease ? DISABLE_RELEASE : beginPos) +{ +} + +void InstrumentSequenceRelease::setType(ReleaseType type) +{ + type_ = type; + if (type == ReleaseType::NoRelease) begin_ = DISABLE_RELEASE; +} +void InstrumentSequenceRelease::setBeginPos(int pos) +{ + if (type_ != ReleaseType::NoRelease) begin_ = pos; +} + +void InstrumentSequenceRelease::disable() +{ + type_ = ReleaseType::NoRelease; + begin_ = DISABLE_RELEASE; +} diff -Nru bambootracker-0.4.5+git20121209/BambooTracker/instrument/sequence_property.hpp bambootracker-0.4.6/BambooTracker/instrument/sequence_property.hpp --- bambootracker-0.4.5+git20121209/BambooTracker/instrument/sequence_property.hpp 1970-01-01 00:00:00.000000000 +0000 +++ bambootracker-0.4.6/BambooTracker/instrument/sequence_property.hpp 2021-02-11 14:37:17.000000000 +0000 @@ -0,0 +1,498 @@ +/* + * Copyright (C) 2018-2020 Rerrah + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +#pragma once +#include +#include +#include +#include +#include +#include +#include "abstract_instrument_property.hpp" +#include "sequence_iterator_interface.hpp" + +struct InstrumentSequenceBaseUnit +{ + int data; + + static constexpr int ERR_DATA = -1; + + InstrumentSequenceBaseUnit() noexcept; + explicit InstrumentSequenceBaseUnit(int d) noexcept; + virtual ~InstrumentSequenceBaseUnit() = default; + + friend bool operator==(const InstrumentSequenceBaseUnit& a, const InstrumentSequenceBaseUnit& b) + { + return a.data == b.data; + } + + friend bool operator!=(const InstrumentSequenceBaseUnit& a, const InstrumentSequenceBaseUnit& b) { return !(a == b); } +}; + +struct InstrumentSequenceExtendUnit : public InstrumentSequenceBaseUnit +{ + enum SubdataType : int + { + UnusedSubdata, + RawSubdata, + RatioSubdata, + ShiftSubdata + } type; + /// - If bit 17 is 0, + /// * If bit 16 is 0, bit 0-15 is raw data + /// * If bit 16 is 1, bit 0-7 is 2nd and bit 8-15 is 1st ratio value + /// - If bit 17 is 1, + /// * If bit 16 is 0, bit 0-15 is right shift value + /// * If bit 16 is 1, bit 0-15 is left shift value + int subdata; + + InstrumentSequenceExtendUnit() noexcept; + explicit InstrumentSequenceExtendUnit(int d, SubdataType subType, int subData) noexcept; + + static InstrumentSequenceExtendUnit makeOnlyDataUnit(int data) noexcept; + static InstrumentSequenceExtendUnit makeRawUnit(int data, int sub) noexcept; + static InstrumentSequenceExtendUnit makeRatioUnit(int data, int subFirst, int subSecond) noexcept; + static InstrumentSequenceExtendUnit makeShiftUnit(int data, int rshift) noexcept; + static InstrumentSequenceExtendUnit makeUnitWithDecode(int data, int subsrc) noexcept; + + void getSubdataAsRaw(int& raw) const noexcept; + void getSubdataAsRatio(int& first, int& second) const noexcept; + void getSubdataAsShift(int& rshift) const noexcept; + + friend bool operator==(const InstrumentSequenceExtendUnit& a, const InstrumentSequenceExtendUnit& b) + { + return a.data == b.data && a.type == b.type && a.subdata == b.subdata; + } + + friend bool operator!=(const InstrumentSequenceExtendUnit& a, const InstrumentSequenceExtendUnit& b) { return !(a == b); } +}; + +class InstrumentSequenceLoop +{ +public: + using Ptr = std::shared_ptr; + + static constexpr int INFINITE_LOOP = 1; + /// Loop in a closed interval [begin, end] + InstrumentSequenceLoop(int begin, int end, int times = INFINITE_LOOP); + + friend bool operator==(const InstrumentSequenceLoop& a, const InstrumentSequenceLoop& b) + { + return (a.begin_ == b.begin_ && a.end_ == b.end_ && a.times_ == b.times_); + } + + friend bool operator!=(const InstrumentSequenceLoop& a, const InstrumentSequenceLoop& b) { return !(a == b); } + + void setBeginPos(int pos); + inline int getBeginPos() const noexcept { return begin_; } + void setEndPos(int pos); + inline int getEndPos() const noexcept { return end_; } + inline void setTimes(int times) noexcept { times_ = times; } + inline int getTimes() const noexcept { return times_; } + inline bool isInfinite() const noexcept { return times_ == INFINITE_LOOP; } + + inline Ptr getInnerLoopBeginAt(int pos) const { return childs_.at(pos); } + + bool addInnerLoop(const InstrumentSequenceLoop& inner); + bool changeInnerLoop(int prevBegin, int prevEnd, const InstrumentSequenceLoop& loop); + void removeInnerLoop(int begin, int end); + void removeAllInnerLoops(); + std::vector getAllInnerLoops()const; + + inline bool hasInnerLoop() const { return !childs_.empty(); } + inline bool hasInnerLoopBeginAt(int pos) const { return childs_.count(pos); } + bool isOverlapped(int begin, int end) const; + inline bool isOverlapped(const InstrumentSequenceLoop& other) const + { + return isOverlapped(other.begin_, other.end_); + } + bool isContainable(int begin, int end) const; + inline bool isContainable(const InstrumentSequenceLoop& other) const + { + return isContainable(other.begin_, other.end_); + } + bool hasSameRegion(int begin, int end) const; + inline bool hasSameRegion(const InstrumentSequenceLoop& other) const + { + return hasSameRegion(other.begin_, other.end_); + } + InstrumentSequenceLoop clone() const; + +protected: + int begin_, end_, times_; + std::map childs_; +}; + +class InstrumentSequenceLoopRoot : public InstrumentSequenceLoop +{ +public: + InstrumentSequenceLoopRoot(int size) + : InstrumentSequenceLoop(0, size - 1) {} + + inline int size() const { return end_; } + inline void extend() { setEndPos(end_ + 1); } + inline void shrink() { if (end_) setEndPos(end_ - 1); } + inline void resize(int size) { setEndPos(size - 1); } + inline void clear() + { + removeAllInnerLoops(); + setEndPos(0); + } + inline Ptr getLoopBeginAt(int pos) const + { + return getInnerLoopBeginAt(pos); + } + inline bool addLoop(const InstrumentSequenceLoop& loop) + { + return addInnerLoop(loop); + } + inline bool changeLoop(int prevBegin, int prevEnd, const InstrumentSequenceLoop& loop) + { + return changeInnerLoop(prevBegin, prevEnd, loop); + } + inline void removeLoop(int begin, int end) + { + removeInnerLoop(begin, end); + } + inline void removeAllLoops() + { + removeAllInnerLoops(); + } + inline std::vector getAllLoops() const + { + return getAllInnerLoops(); + } + inline bool hasLoop() const + { + return hasInnerLoop(); + } + inline bool hasLoopBeginAt(int pos) const + { + return hasInnerLoopBeginAt(pos); + } + InstrumentSequenceLoopRoot clone() const; +}; + +namespace inst_utils +{ +class LoopStack +{ +private: + struct StackItem + { + InstrumentSequenceLoop::Ptr loop; + int count; + bool isInfinite; + + explicit StackItem(const InstrumentSequenceLoop::Ptr& ptr); + }; + std::deque stack_; + +public: + explicit LoopStack(const std::shared_ptr& ptr); + void clear(); + void pushLoopsAtPos(int pos); + int checkLoopEndAndNextPos(int curPos); +}; +} + +class InstrumentSequenceRelease +{ +public: + enum ReleaseType + { + NoRelease, + FixedRelease, + AbsoluteRelease, + RelativeRelease + }; + + static constexpr int DISABLE_RELEASE = -1; + + explicit InstrumentSequenceRelease(ReleaseType getType, int getBeginPos = DISABLE_RELEASE); + + friend bool operator==(const InstrumentSequenceRelease& a, const InstrumentSequenceRelease& b) + { + return (a.type_ == b.type_ && a.begin_ == b.begin_); + } + + friend bool operator!=(const InstrumentSequenceRelease& a, const InstrumentSequenceRelease& b) { return !(a == b); } + + inline ReleaseType getType() const noexcept { return type_; } + void setType(ReleaseType type); + void setBeginPos(int pos); + inline int getBeginPos() const noexcept { return begin_; } + + inline bool isEnabled() const noexcept + { + return (type_ != ReleaseType::NoRelease && begin_ != DISABLE_RELEASE); + } + + void disable(); + +private: + ReleaseType type_; + int begin_; +}; + +template +class InstrumentSequenceProperty final : public AbstractInstrumentProperty +{ +public: + InstrumentSequenceProperty(int num, SequenceType defaultType, const T& defaultUnit, const T& errorUnit, int relReleaseDenom = 1) + : AbstractInstrumentProperty(num), + DEF_TYPE_(defaultType), + DEF_UNIT_(defaultUnit), + ERR_UNIT_(errorUnit), + REL_RELEASE_DENOM_(relReleaseDenom), + loop_(std::make_shared(1)), + release_(InstrumentSequenceRelease::NoRelease) + { + clearParameters(); + } + + friend bool operator==(const InstrumentSequenceProperty& a, const InstrumentSequenceProperty& b) + { + return (a.type_ == b.type_ && a.seq_ == b.seq_ && a.loop_ == b.loop_ && a.release_ == b.release_); + } + + friend bool operator!=(const InstrumentSequenceProperty& a, const InstrumentSequenceProperty& b) { return !(a == b); } + + std::unique_ptr clone() + { + std::unique_ptr clone = std::make_unique(*this); + clone->clearUserInstruments(); + return clone; + } + + inline void setType(SequenceType type) noexcept { type_ = type; } + inline SequenceType getType() const noexcept { return type_; } + + bool isEdited() const override + { + return (seq_.size() > 1 || seq_.front() != DEF_UNIT_ || loop_->hasLoop() || release_.getType() != InstrumentSequenceRelease::NoRelease); + } + + void clearParameters() override + { + type_ = DEF_TYPE_; + seq_ = { DEF_UNIT_ }; + loop_->clear(); + release_.disable(); + } + + //***** Sequence ***** + inline size_t getSequenceSize() const { return seq_.size(); } + T getSequenceUnit(int n) const { return seq_.at(static_cast(n)); } + inline std::vector getSequence() const { return seq_; } + + void addSequenceUnit(const T& unit) { + seq_.push_back(unit); + loop_->extend(); + } + + void removeSequenceUnit() + { + seq_.pop_back(); + loop_->shrink(); + if (release_.getBeginPos() == static_cast(seq_.size())) + release_.disable(); + } + + void setSequenceUnit(int n, const T& unit) { seq_.at(static_cast(n)) = unit; } + + //***** Loop ***** + inline InstrumentSequenceLoopRoot getLoopRoot() const { return *loop_; } + inline void addLoop(const InstrumentSequenceLoop& loop) const { loop_->addLoop(loop); } + inline void removeLoop(int begin, int end) const { loop_->removeLoop(begin, end); } + inline void clearLoops() const { loop_->removeAllLoops(); } + inline void changeLoop(int prevBegin, int prevEnd, const InstrumentSequenceLoop& loop) + { + loop_->changeLoop(prevBegin, prevEnd, loop); + } + + //***** Release ***** + InstrumentSequenceRelease getRelease() const noexcept { return release_; } + inline void setRelease(const InstrumentSequenceRelease& release) { release_ = release; } + + class Iterator final : public SequenceIteratorInterface + { + public: + explicit Iterator(const InstrumentSequenceProperty* seqProp) + : SequenceIteratorInterface(0), + seqProp_(seqProp), + started_(false), + loopStack_(seqProp->loop_), + isRelease_(false), + relReleaseRate_(1.) + { + } + + SequenceType type() const override + { + return seqProp_->type_; + } + + T data() const override + { + if (this->hasEnded() || static_cast(seqProp_->seq_.size()) <= this->pos_) + return seqProp_->ERR_UNIT_; + return (isRelease_ ? seqProp_->getSequenceUnit(this->pos_, relReleaseRate_) + : seqProp_->getSequenceUnit(this->pos_)); + } + + int next() override + { + if (this->hasEnded()) return this->END_SEQ_POS; + + if (!started_) { + started_ = true; + return this->pos_; + } + + this->pos_ = loopStack_.checkLoopEndAndNextPos(this->pos_); + loopStack_.pushLoopsAtPos(this->pos_); + + // Range check + if ((!isRelease_ && seqProp_->release_.isEnabled() && this->pos_ >= seqProp_->release_.getBeginPos()) + || this->pos_ >= static_cast(seqProp_->seq_.size())) { + this->pos_ = this->END_SEQ_POS; + } + + return this->pos_; + } + + int front() override + { + started_ = true; + loopStack_.clear(); + isRelease_ = false; + relReleaseRate_ = 1.; + + if (seqProp_->release_.getBeginPos()) { + this->pos_ = 0; + loopStack_.pushLoopsAtPos(0); + } + else { + this->pos_ = this->END_SEQ_POS; + } + + return this->pos_; + } + + int release() override + { + const std::vector& seq = seqProp_->seq_; + const InstrumentSequenceRelease& release = seqProp_->release_; + + int next = this->END_SEQ_POS; + isRelease_ = true; + loopStack_.clear(); + switch (release.getType()) { + case InstrumentSequenceRelease::NoRelease: + break; + case InstrumentSequenceRelease::FixedRelease: + { + next = release.getBeginPos(); + break; + } + case InstrumentSequenceRelease::AbsoluteRelease: + { + int crtr; + if (this->pos_ == this->END_SEQ_POS) { + if (int relBegin = release.getBeginPos()) { + crtr = seq[static_cast(relBegin - 1)].data; + } + else { + next = relBegin; + break; + } + } + else { + crtr = seq[static_cast(this->pos_)].data; + } + + auto&& it = std::find_if(seq.begin() + release.getBeginPos(), seq.end(), + [crtr](const T& unit) { return (unit.data <= crtr); }); + if (it != seq.end()) next = std::distance(seq.begin(), it); + break; + } + case InstrumentSequenceRelease::RelativeRelease: + { + next = release.getBeginPos(); + if (this->hasEnded()) { + if (next) relReleaseRate_ = seq[static_cast(next - 1)].data / seqProp_->REL_RELEASE_DENOM_; + } + else { + relReleaseRate_ = seq[static_cast(this->pos_)].data / seqProp_->REL_RELEASE_DENOM_; + } + break; + } + } + this->pos_ = next; + + if (next != this->END_SEQ_POS) loopStack_.pushLoopsAtPos(this->pos_); + + return this->pos_; + } + + int end() override + { + this->pos_ = this->END_SEQ_POS; + started_ = false; + return this->END_SEQ_POS; + } + + private: + const InstrumentSequenceProperty* seqProp_; + bool started_; + inst_utils::LoopStack loopStack_; + bool isRelease_; + double relReleaseRate_; + }; + + std::unique_ptr getIterator() + { + return std::make_unique(this); + } + +private: + const SequenceType DEF_TYPE_; + const T DEF_UNIT_; + const T ERR_UNIT_; + const double REL_RELEASE_DENOM_; + + SequenceType type_; + std::vector seq_; + std::shared_ptr loop_; + InstrumentSequenceRelease release_; + + inline T getSequenceUnit(int n, double relRate) const + { + T unit = seq_.at(static_cast(n)); + unit.data = static_cast(unit.data * relRate); + return unit; + } +}; diff -Nru bambootracker-0.4.5+git20121209/BambooTracker/instrument/waveform_adpcm.cpp bambootracker-0.4.6/BambooTracker/instrument/waveform_adpcm.cpp --- bambootracker-0.4.5+git20121209/BambooTracker/instrument/waveform_adpcm.cpp 2020-12-08 14:05:28.000000000 +0000 +++ bambootracker-0.4.6/BambooTracker/instrument/waveform_adpcm.cpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,141 +0,0 @@ -/* - * Copyright (C) 2020 Rerrah - * - * Permission is hereby granted, free of charge, to any person - * obtaining a copy of this software and associated documentation - * files (the "Software"), to deal in the Software without - * restriction, including without limitation the rights to use, - * copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following - * conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES - * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT - * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, - * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR - * OTHER DEALINGS IN THE SOFTWARE. - */ - -#include "waveform_adpcm.hpp" - -SampleADPCM::SampleADPCM(int num) - : AbstractInstrumentProperty (num) -{ - clearParameters(); -} - -SampleADPCM::SampleADPCM(const SampleADPCM& other) - : AbstractInstrumentProperty(other) -{ - rootKeyNum_ = other.rootKeyNum_; - rootDeltaN_ = other.rootDeltaN_; - isRepeated_ = other.isRepeated_; - sample_ = other.sample_; -} - -bool operator==(const SampleADPCM& a, const SampleADPCM& b) { - return (a.rootKeyNum_ == b.rootKeyNum_ && a.rootDeltaN_ == b.rootDeltaN_ - && a.isRepeated_ == b.isRepeated_ && a.sample_ == b.sample_); -} - -std::unique_ptr SampleADPCM::clone() -{ - std::unique_ptr clone = std::make_unique(*this); - clone->clearUserInstruments(); - return clone; -} - -void SampleADPCM::setRootKeyNumber(int n) -{ - rootKeyNum_ = n; -} - -int SampleADPCM::getRootKeyNumber() const -{ - return rootKeyNum_; -} - -void SampleADPCM::setRootDeltaN(int dn) -{ - rootDeltaN_ = dn; -} - -int SampleADPCM::getRootDeltaN() const -{ - return rootDeltaN_; -} - -void SampleADPCM::setRepeatEnabled(bool enabled) -{ - isRepeated_ = enabled; -} - -bool SampleADPCM::isRepeatable() const -{ - return isRepeated_; -} - -void SampleADPCM::storeSample(std::vector sample) -{ - sample_ = sample; -} - -std::vector SampleADPCM::getSamples() const -{ - return sample_; -} - -void SampleADPCM::clearSample() -{ - startAddress_ = 0; - stopAddress_ = 0; - sample_ = std::vector(1); -} - -void SampleADPCM::setStartAddress(size_t addr) -{ - startAddress_ = addr; -} - -size_t SampleADPCM::getStartAddress() const -{ - return startAddress_; -} - -void SampleADPCM::setStopAddress(size_t addr) -{ - stopAddress_ = addr; -} - -size_t SampleADPCM::getStopAddress() const -{ - return stopAddress_; -} - -bool SampleADPCM::isEdited() const -{ - if (rootKeyNum_ != DEF_RT_KEY_ - || rootDeltaN_ != DEF_RT_DELTAN_ - || isRepeated_ != DEF_REPET_ - || sample_.size() != 1 - || sample_[0] != 0) - return true; - return false; -} - -void SampleADPCM::clearParameters() -{ - rootKeyNum_ = DEF_RT_KEY_; - rootDeltaN_ = DEF_RT_DELTAN_; - isRepeated_ = DEF_REPET_; - sample_ = std::vector(1); - startAddress_ = 0; - stopAddress_ = 0; -} diff -Nru bambootracker-0.4.5+git20121209/BambooTracker/instrument/waveform_adpcm.hpp bambootracker-0.4.6/BambooTracker/instrument/waveform_adpcm.hpp --- bambootracker-0.4.5+git20121209/BambooTracker/instrument/waveform_adpcm.hpp 2020-12-08 14:05:28.000000000 +0000 +++ bambootracker-0.4.6/BambooTracker/instrument/waveform_adpcm.hpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,70 +0,0 @@ -/* - * Copyright (C) 2020 Rerrah - * - * Permission is hereby granted, free of charge, to any person - * obtaining a copy of this software and associated documentation - * files (the "Software"), to deal in the Software without - * restriction, including without limitation the rights to use, - * copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following - * conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES - * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT - * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, - * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR - * OTHER DEALINGS IN THE SOFTWARE. - */ - -#pragma once - -#include -#include -#include -#include "abstract_instrument_property.hpp" - -class SampleADPCM : public AbstractInstrumentProperty -{ -public: - explicit SampleADPCM(int num); - SampleADPCM(const SampleADPCM& other); - - friend bool operator==(const SampleADPCM& a, const SampleADPCM& b); - friend bool operator!=(const SampleADPCM& a, const SampleADPCM& b) { return !(a == b); } - - std::unique_ptr clone(); - - void setRootKeyNumber(int n); - int getRootKeyNumber() const; - void setRootDeltaN(int dn); - int getRootDeltaN() const; - void setRepeatEnabled(bool enabled); - bool isRepeatable() const; - void storeSample(std::vector sample); - std::vector getSamples() const; - void clearSample(); - void setStartAddress(size_t addr); - size_t getStartAddress() const; - void setStopAddress(size_t addr); - size_t getStopAddress() const; - - bool isEdited() const override; - void clearParameters() override; - -private: - int rootKeyNum_, rootDeltaN_; - bool isRepeated_; - std::vector sample_; - size_t startAddress_, stopAddress_; - - static constexpr int DEF_RT_KEY_ = 60; // C5 - static constexpr int DEF_RT_DELTAN_ = 0x49cd; // 16000Hz - static constexpr bool DEF_REPET_ = false; -}; diff -Nru bambootracker-0.4.5+git20121209/BambooTracker/io/bank_io.cpp bambootracker-0.4.6/BambooTracker/io/bank_io.cpp --- bambootracker-0.4.5+git20121209/BambooTracker/io/bank_io.cpp 2020-12-08 14:05:28.000000000 +0000 +++ bambootracker-0.4.6/BambooTracker/io/bank_io.cpp 2021-02-11 14:37:17.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (C) 2019-2020 Rerrah + * Copyright (C) 2019-2021 Rerrah * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation @@ -30,7 +30,10 @@ #include "wopn_io.hpp" #include "ff_io.hpp" #include "ppc_io.hpp" +#include "p86_io.hpp" +#include "pps_io.hpp" #include "pvi_io.hpp" +#include "pzi_io.hpp" #include "dat_io.hpp" namespace io @@ -60,7 +63,10 @@ handler_.add(new WopnIO); handler_.add(new FfIO); handler_.add(new PpcIO); + handler_.add(new P86IO); + handler_.add(new PpsIO); handler_.add(new PviIO); + handler_.add(new PziIO); handler_.add(new DatIO); } diff -Nru bambootracker-0.4.5+git20121209/BambooTracker/io/bank_io.hpp bambootracker-0.4.6/BambooTracker/io/bank_io.hpp --- bambootracker-0.4.5+git20121209/BambooTracker/io/bank_io.hpp 2020-12-08 14:05:28.000000000 +0000 +++ bambootracker-0.4.6/BambooTracker/io/bank_io.hpp 2021-02-11 14:37:17.000000000 +0000 @@ -38,7 +38,7 @@ class AbstractBankIO { public: - AbstractBankIO(std::string ext, std::string desc, bool loadable, bool savable) + AbstractBankIO(const std::string& ext, const std::string& desc, bool loadable, bool savable) : ext_(ext), desc_(desc), loadable_(loadable), savable_(savable) {} virtual ~AbstractBankIO() = default; virtual AbstractBank* load(const BinaryContainer& ctr) const; @@ -50,7 +50,7 @@ inline bool isSavable() const noexcept { return savable_; } private: - std::string ext_, desc_; + const std::string ext_, desc_; bool loadable_, savable_; }; diff -Nru bambootracker-0.4.5+git20121209/BambooTracker/io/binary_container.cpp bambootracker-0.4.6/BambooTracker/io/binary_container.cpp --- bambootracker-0.4.5+git20121209/BambooTracker/io/binary_container.cpp 2020-12-08 14:05:28.000000000 +0000 +++ bambootracker-0.4.6/BambooTracker/io/binary_container.cpp 2021-02-11 14:37:17.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (C) 2018-2020 Rerrah + * Copyright (C) 2018-2021 Rerrah * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation @@ -30,21 +30,21 @@ namespace io { -BinaryContainer::BinaryContainer(size_t defCapacity) +BinaryContainer::BinaryContainer() : isLE_(true) { - if (defCapacity) reserve(defCapacity); } -BinaryContainer::BinaryContainer(std::vector buf) - : buf_(buf), - isLE_(true) +BinaryContainer::BinaryContainer(const std::vector& buf) + : isLE_(true) { + std::copy(buf.cbegin(), buf.cend(), std::back_inserter(buf_)); } -size_t BinaryContainer::size() const noexcept +BinaryContainer::BinaryContainer(std::vector&& buf) + : isLE_(true) { - return buf_.size(); + std::move(buf.begin(), buf.end(), std::back_inserter(buf_)); } void BinaryContainer::clear() @@ -53,241 +53,225 @@ buf_.shrink_to_fit(); } -void BinaryContainer::reserve(size_t capacity) -{ - buf_.reserve(capacity); -} - -void BinaryContainer::setEndian(bool isLittleEndian) noexcept +void BinaryContainer::appendInt8(int8_t v) { - isLE_ = isLittleEndian; + buf_.push_back(static_cast(v)); } -bool BinaryContainer::isLittleEndian() const noexcept +void BinaryContainer::appendUint8(uint8_t v) { - return isLE_; + buf_.push_back(v); } -void BinaryContainer::appendInt8(const int8_t v) -{ - buf_.push_back(static_cast(v)); -} - -void BinaryContainer::appendUint8(const uint8_t v) -{ - buf_.push_back(static_cast(v)); -} - -void BinaryContainer::appendInt16(const int16_t v) +void BinaryContainer::appendInt16(int16_t v) { append({ - static_cast(0x00ff & v), - static_cast(v >> 8), + static_cast(0x00ff & v), + static_cast(v >> 8), }); } -void BinaryContainer::appendUint16(const uint16_t v) +void BinaryContainer::appendUint16(uint16_t v) { append({ - static_cast(0x00ff & v), - static_cast(v >> 8), + static_cast(0x00ff & v), + static_cast(v >> 8), }); } -void BinaryContainer::appendInt32(const int32_t v) +void BinaryContainer::appendInt32(int32_t v) { append({ - static_cast(0x000000ff & v), - static_cast(0x0000ff & (v >> 8)), - static_cast(0x00ff & (v >> 16)), - static_cast(v >> 24), + static_cast(0x000000ff & v), + static_cast(0x0000ff & (v >> 8)), + static_cast(0x00ff & (v >> 16)), + static_cast(v >> 24), }); } -void BinaryContainer::appendUint32(const uint32_t v) +void BinaryContainer::appendUint32(uint32_t v) { append({ - static_cast(0x000000ff & v), - static_cast(0x0000ff & (v >> 8)), - static_cast(0x00ff & (v >> 16)), - static_cast(v >> 24), + static_cast(0x000000ff & v), + static_cast(0x0000ff & (v >> 8)), + static_cast(0x00ff & (v >> 16)), + static_cast(v >> 24), }); } -void BinaryContainer::appendChar(const char c) +void BinaryContainer::appendChar(char c) { - buf_.push_back(c); + buf_.push_back(static_cast(c)); } -void BinaryContainer::appendString(const std::string str) +void BinaryContainer::appendString(const std::string& str) { - std::copy(str.begin(), str.end(), std::back_inserter(buf_)); + std::copy(str.cbegin(), str.cend(), std::back_inserter(buf_)); } -void BinaryContainer::appendArray(const uint8_t* array, size_t size) +void BinaryContainer::appendArray(const uint8_t* array, size_type size) { std::copy(array, array + size, std::back_inserter(buf_)); } void BinaryContainer::appendVector(const std::vector& vec) { - std::copy(vec.begin(), vec.end(), std::back_inserter(buf_)); + std::copy(vec.cbegin(), vec.cend(), std::back_inserter(buf_)); } -void BinaryContainer::appendVector(const std::vector& vec) +void BinaryContainer::appendVector(std::vector&& vec) { - std::copy(vec.begin(), vec.end(), std::back_inserter(buf_)); + std::move(vec.begin(), vec.end(), std::back_inserter(buf_)); } void BinaryContainer::appendBinaryContainer(const BinaryContainer& bc) { - const char* bcp = bc.getPointer(); - std::copy(bcp, bcp + bc.size(), std::back_inserter(buf_)); + std::copy(bc.cbegin(), bc.cend(), std::back_inserter(buf_)); +} + +void BinaryContainer::appendBinaryContainer(BinaryContainer&& bc) +{ + std::move(bc.begin(), bc.end(), std::back_inserter(buf_)); } -void BinaryContainer::writeInt8(size_t offset, const int8_t v) +void BinaryContainer::writeInt8(size_type offset, int8_t v) { - buf_.at(offset) = static_cast(v); + buf_.at(offset) = static_cast(v); } -void BinaryContainer::writeUint8(size_t offset, const uint8_t v) +void BinaryContainer::writeUint8(size_type offset, uint8_t v) { - buf_.at(offset) = static_cast(v); + buf_.at(offset) = v; } -void BinaryContainer::writeInt16(size_t offset, const int16_t v) +void BinaryContainer::writeInt16(size_type offset, int16_t v) { write(offset, { - static_cast(0x00ff & v), - static_cast(v >> 8), + static_cast(0x00ff & v), + static_cast(v >> 8), }); } -void BinaryContainer::writeUint16(size_t offset, const uint16_t v) +void BinaryContainer::writeUint16(size_type offset, uint16_t v) { write(offset, { - static_cast(0x00ff & v), - static_cast(v >> 8), + static_cast(0x00ff & v), + static_cast(v >> 8), }); } -void BinaryContainer::writeInt32(size_t offset, const int32_t v) +void BinaryContainer::writeInt32(size_type offset, int32_t v) { write(offset, { - static_cast(0x000000ff & v), - static_cast(0x0000ff & (v >> 8)), - static_cast(0x00ff & (v >> 16)), - static_cast(v >> 24), + static_cast(0x000000ff & v), + static_cast(0x0000ff & (v >> 8)), + static_cast(0x00ff & (v >> 16)), + static_cast(v >> 24), }); } -void BinaryContainer::writeUint32(size_t offset, const uint32_t v) +void BinaryContainer::writeUint32(size_type offset, uint32_t v) { write(offset, { - static_cast(0x000000ff & v), - static_cast(0x0000ff & (v >> 8)), - static_cast(0x00ff & (v >> 16)), - static_cast(v >> 24), + static_cast(0x000000ff & v), + static_cast(0x0000ff & (v >> 8)), + static_cast(0x00ff & (v >> 16)), + static_cast(v >> 24), }); } -void BinaryContainer::writeChar(size_t offset, const char c) +void BinaryContainer::writeChar(size_type offset, char c) { - buf_.at(offset) = c; + buf_.at(offset) = static_cast(c); } -void BinaryContainer::writeString(size_t offset, const std::string str) +void BinaryContainer::writeString(size_type offset, const std::string& str) { - std::copy(str.begin(), str.end(), buf_.begin() + static_cast(offset)); + std::copy(str.cbegin(), str.cend(), buf_.begin() + static_cast(offset)); } -int8_t BinaryContainer::readInt8(size_t offset) const +int8_t BinaryContainer::readInt8(size_type offset) const { return static_cast(buf_.at(offset)); } -uint8_t BinaryContainer::readUint8(size_t offset) const +uint8_t BinaryContainer::readUint8(size_type offset) const { return static_cast(buf_.at(offset)); } -int16_t BinaryContainer::readInt16(size_t offset) const +int16_t BinaryContainer::readInt16(size_type offset) const { - std::vector data = read(offset, 2); + std::vector data = read(offset, 2); return static_cast(data[0] | (data[1] << 8)); } -uint16_t BinaryContainer::readUint16(size_t offset) const +uint16_t BinaryContainer::readUint16(size_type offset) const { - std::vector data = read(offset, 2); + std::vector data = read(offset, 2); return static_cast(data[0] | (data[1] << 8)); } -int32_t BinaryContainer::readInt32(size_t offset) const +int32_t BinaryContainer::readInt32(size_type offset) const { - std::vector data = read(offset, 4); + std::vector data = read(offset, 4); return static_cast(data[0] | (data[1] << 8) | (data[2] << 16) | (data[3] << 24)); } -uint32_t BinaryContainer::readUint32(size_t offset) const +uint32_t BinaryContainer::readUint32(size_type offset) const { - std::vector data = read(offset, 4); + std::vector data = read(offset, 4); return static_cast(data[0] | (data[1] << 8) | (data[2] << 16) | (data[3] << 24)); } -char BinaryContainer::readChar(size_t offset) const +char BinaryContainer::readChar(size_type offset) const { return buf_.at(offset); } -std::string BinaryContainer::readString(size_t offset, size_t length) const +std::string BinaryContainer::readString(size_type offset, size_type length) const { return std::string(buf_.begin() + static_cast(offset), buf_.begin() + static_cast(offset + length)); } -BinaryContainer BinaryContainer::getSubcontainer(size_t offset, size_t length) const -{ - std::vector tmp; - std::copy_n(buf_.begin() + static_cast(offset), length, std::back_inserter(tmp)); - return BinaryContainer(std::move(tmp)); -} - -const char* BinaryContainer::getPointer() const +BinaryContainer BinaryContainer::getSubcontainer(size_type offset, size_type length) const { - return buf_.data(); + BinaryContainer sub; + std::copy_n(buf_.begin() + static_cast(offset), length, std::back_inserter(sub)); + return sub; } std::vector BinaryContainer::toVector() const { std::vector vec; - std::transform(buf_.begin(), buf_.end(), std::back_inserter(vec), - [](const char v) { return static_cast(v); }); + vec.reserve(buf_.size()); + std::copy(buf_.cbegin(), buf_.cend(), std::back_inserter(vec)); return vec; } -void BinaryContainer::append(std::vector a) +void BinaryContainer::append(const std::vector&& a) { if (isLE_) - std::copy(a.begin(), a.end(), std::back_inserter(buf_)); + std::copy(a.cbegin(), a.cend(), std::back_inserter(buf_)); else - std::reverse_copy(a.begin(), a.end(), std::back_inserter(buf_)); + std::reverse_copy(a.cbegin(), a.cend(), std::back_inserter(buf_)); } -void BinaryContainer::write(size_t offset, std::vector a) +void BinaryContainer::write(size_t offset, const std::vector&& a) { if (isLE_) - std::copy(a.begin(), a.end(), buf_.begin() + static_cast(offset)); + std::copy(a.cbegin(), a.cend(), buf_.begin() + static_cast(offset)); else - std::reverse_copy(a.begin(), a.end(), buf_.begin() + static_cast(offset)); + std::reverse_copy(a.cbegin(), a.cend(), buf_.begin() + static_cast(offset)); } -std::vector BinaryContainer::read(size_t offset, size_t size) const +std::vector BinaryContainer::read(size_type offset, size_type size) const { - std::vector data; + std::vector data; if (isLE_) - std::copy(buf_.begin() + static_cast(offset), buf_.begin() + static_cast(offset + size), std::back_inserter(data)); + std::copy(buf_.cbegin() + static_cast(offset), buf_.cbegin() + static_cast(offset + size), std::back_inserter(data)); else - std::reverse_copy(buf_.begin() + static_cast(offset), buf_.begin() + static_cast(offset + size), std::back_inserter(data)); + std::reverse_copy(buf_.cbegin() + static_cast(offset), buf_.cbegin() + static_cast(offset + size), std::back_inserter(data)); return data; } } diff -Nru bambootracker-0.4.5+git20121209/BambooTracker/io/binary_container.hpp bambootracker-0.4.6/BambooTracker/io/binary_container.hpp --- bambootracker-0.4.5+git20121209/BambooTracker/io/binary_container.hpp 2020-12-08 14:05:28.000000000 +0000 +++ bambootracker-0.4.6/BambooTracker/io/binary_container.hpp 2021-02-11 14:37:17.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (C) 2018-2020 Rerrah + * Copyright (C) 2018-2021 Rerrah * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation @@ -25,6 +25,7 @@ #pragma once +#include #include #include #include @@ -34,57 +35,87 @@ class BinaryContainer { public: - explicit BinaryContainer(size_t defCapacity = 0); - explicit BinaryContainer(std::vector buf); - size_t size() const noexcept; + using container_type = std::deque; + using value_type = container_type::value_type; + using size_type = container_type::size_type; + using iterator = container_type::iterator; + using const_iterator = container_type::const_iterator; + using reverse_iterator = container_type::reverse_iterator; + using const_reverse_iterator = container_type::const_reverse_iterator; + + explicit BinaryContainer(); + explicit BinaryContainer(const std::vector& buf); + explicit BinaryContainer(std::vector&& buf); + + inline iterator begin() noexcept { return buf_.begin(); } + inline const_iterator begin() const noexcept { return buf_.begin(); } + + inline iterator end() noexcept { return buf_.end(); } + inline const_iterator end() const noexcept { return buf_.end(); } + + inline const_iterator cbegin() const noexcept { return buf_.cbegin(); } + inline const_iterator cend() const noexcept { return buf_.cend(); } + + inline reverse_iterator rbegin() noexcept { return buf_.rbegin(); } + inline const_reverse_iterator rbegin() const noexcept { return buf_.rbegin(); } + + inline reverse_iterator rend() noexcept { return buf_.rend(); } + inline const_reverse_iterator rend() const noexcept { return buf_.rend(); } + + inline const_reverse_iterator crbegin() const noexcept { return buf_.crbegin(); } + inline const_reverse_iterator crend() const noexcept { return buf_.crend(); } + + inline void push_back(uint8_t v) { appendUint8(v); } + + inline size_type size() const noexcept { return buf_.size(); } void clear(); - void reserve(size_t capacity); + inline void resize(size_type size) { buf_.resize(size); } - void setEndian(bool isLittleEndian) noexcept; - bool isLittleEndian() const noexcept; + inline void setEndian(bool isLittleEndian) noexcept { isLE_ = isLittleEndian; } + inline bool isLittleEndian() const noexcept { return isLE_; } - void appendInt8(const int8_t v); - void appendUint8(const uint8_t v); - void appendInt16(const int16_t v); - void appendUint16(const uint16_t v); - void appendInt32(const int32_t v); - void appendUint32(const uint32_t v); - void appendChar(const char c); - void appendString(const std::string str); - void appendArray(const uint8_t* array, size_t size); + void appendInt8(int8_t v); + void appendUint8(uint8_t v); + void appendInt16(int16_t v); + void appendUint16(uint16_t v); + void appendInt32(int32_t v); + void appendUint32(uint32_t v); + void appendChar(char c); + void appendString(const std::string& str); + void appendArray(const uint8_t* array, size_type size); void appendVector(const std::vector& vec); - void appendVector(const std::vector& vec); + void appendVector(std::vector&& vec); void appendBinaryContainer(const BinaryContainer& bc); + void appendBinaryContainer(BinaryContainer&& bc); - void writeInt8(size_t offset, const int8_t v); - void writeUint8(size_t offset, const uint8_t v); - void writeInt16(size_t offset, const int16_t v); - void writeUint16(size_t offset, const uint16_t v); - void writeInt32(size_t offset, const int32_t v); - void writeUint32(size_t offset, const uint32_t v); - void writeChar(size_t offset, const char c); - void writeString(size_t offset, const std::string str); - - int8_t readInt8(size_t offset) const; - uint8_t readUint8(size_t offset) const; - int16_t readInt16(size_t offset) const; - uint16_t readUint16(size_t offset) const; - int32_t readInt32(size_t offset) const; - uint32_t readUint32(size_t offset) const; - char readChar(size_t offset) const; - std::string readString(size_t offset, size_t length) const; + void writeInt8(size_type offset, int8_t v); + void writeUint8(size_type offset, uint8_t v); + void writeInt16(size_type offset, int16_t v); + void writeUint16(size_type offset, uint16_t v); + void writeInt32(size_type offset, int32_t v); + void writeUint32(size_type offset, uint32_t v); + void writeChar(size_type offset, char c); + void writeString(size_type offset, const std::string& str); + + int8_t readInt8(size_type offset) const; + uint8_t readUint8(size_type offset) const; + int16_t readInt16(size_type offset) const; + uint16_t readUint16(size_type offset) const; + int32_t readInt32(size_type offset) const; + uint32_t readUint32(size_type offset) const; + char readChar(size_type offset) const; + std::string readString(size_type offset, size_type length) const; - BinaryContainer getSubcontainer(size_t offset, size_t length) const; + BinaryContainer getSubcontainer(size_type offset, size_type length) const; - const char* getPointer() const; std::vector toVector() const; private: - std::vector buf_; + container_type buf_; bool isLE_; - void append(std::vector a); - void write(size_t offset, std::vector a); - std::vector read(size_t offset, size_t size) const; + void append(const std::vector&& a); + void write(size_t offset, const std::vector&& a); + std::vector read(size_type offset, size_type size) const; }; } diff -Nru bambootracker-0.4.5+git20121209/BambooTracker/io/btb_io.cpp bambootracker-0.4.6/BambooTracker/io/btb_io.cpp --- bambootracker-0.4.5+git20121209/BambooTracker/io/btb_io.cpp 2020-12-08 14:05:28.000000000 +0000 +++ bambootracker-0.4.6/BambooTracker/io/btb_io.cpp 2021-02-11 14:37:17.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (C) 2020 Rerrah + * Copyright (C) 2020-2021 Rerrah * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation @@ -25,11 +25,11 @@ #include "btb_io.hpp" #include +#include #include #include #include #include -#include #include "enum_hash.hpp" #include "version.hpp" #include "instrument.hpp" @@ -38,6 +38,16 @@ namespace io { +namespace +{ +bool judgeRequiredProperty(const std::multiset& usedInstIdcs, const std::vector& reqInstIdcs) +{ + std::vector intrsct; + std::set_intersection(usedInstIdcs.begin(), usedInstIdcs.end(), reqInstIdcs.begin(), reqInstIdcs.end(), std::back_inserter(intrsct)); + return !intrsct.empty(); +} +} + BtbIO::BtbIO() : AbstractBankIO("btb", "BambooTracker bank", true, true) {} AbstractBank* BtbIO::load(const BinaryContainer& ctr) const @@ -77,7 +87,7 @@ std::string name = u8""; if (nameLen > 0) { name = ctr.readString(iCsr, nameLen); - iCsr += nameLen; + /* iCsr += nameLen; */ } names.push_back(name); instCsr += iOfs; // Jump to next @@ -93,7 +103,7 @@ size_t instPropOfs = ctr.readUint32(globCsr); BinaryContainer propCtr = ctr.getSubcontainer(globCsr + 4, instPropOfs - 4); - return new BtBank(std::move(ids), std::move(names), std::move(instCtrs), std::move(propCtr), fileVersion); + return new BtBank(ids, names, instCtrs, propCtr, fileVersion); } void BtbIO::save(BinaryContainer& ctr, const std::weak_ptr instMan, @@ -208,10 +218,8 @@ // FM envelope std::vector envFMIdcs; for (auto& idx : instMan.lock()->getEnvelopeFMEntriedIndices()) { - std::vector users = instMan.lock()->getEnvelopeFMUsers(idx); - std::vector intersection; - std::set_intersection(users.begin(), users.end(), instNums.begin(), instNums.end(), std::back_inserter(intersection)); - if (!intersection.empty()) envFMIdcs.push_back(idx); + if (judgeRequiredProperty(instMan.lock()->getEnvelopeFMUsers(idx), instNums)) + envFMIdcs.push_back(idx); } if (!envFMIdcs.empty()) { ctr.appendUint8(0x00); @@ -252,10 +260,8 @@ // FM LFO std::vector lfoFMIdcs; for (auto& idx : instMan.lock()->getLFOFMEntriedIndices()) { - std::vector users = instMan.lock()->getLFOFMUsers(idx); - std::vector intersection; - std::set_intersection(users.begin(), users.end(), instNums.begin(), instNums.end(), std::back_inserter(intersection)); - if (!intersection.empty()) lfoFMIdcs.push_back(idx); + if (judgeRequiredProperty(instMan.lock()->getLFOFMUsers(idx), instNums)) + lfoFMIdcs.push_back(idx); } if (!lfoFMIdcs.empty()) { ctr.appendUint8(0x01); @@ -283,10 +289,8 @@ for (size_t i = 0; i < 38; ++i) { std::vector idcs; for (auto& idx : instMan.lock()->getOperatorSequenceFMEntriedIndices(FM_OPSEQ_PARAMS[i])) { - std::vector users = instMan.lock()->getOperatorSequenceFMUsers(FM_OPSEQ_PARAMS[i], idx); - std::vector intersection; - std::set_intersection(users.begin(), users.end(), instNums.begin(), instNums.end(), std::back_inserter(intersection)); - if (!intersection.empty()) idcs.push_back(idx); + if (judgeRequiredProperty(instMan.lock()->getOperatorSequenceFMUsers(FM_OPSEQ_PARAMS[i], idx), instNums)) + idcs.push_back(idx); } if (!idcs.empty()) { ctr.appendUint8(0x02 + static_cast(i)); @@ -297,32 +301,32 @@ ctr.appendUint16(0); // Dummy offset auto seq = instMan.lock()->getOperatorSequenceFMSequence(FM_OPSEQ_PARAMS[i], idx); ctr.appendUint16(static_cast(seq.size())); - for (auto& com : seq) { - ctr.appendUint16(static_cast(com.type)); + for (auto& unit : seq) { + ctr.appendUint16(static_cast(unit.data)); } - auto loop = instMan.lock()->getOperatorSequenceFMLoops(FM_OPSEQ_PARAMS[i], idx); - ctr.appendUint16(static_cast(loop.size())); - for (auto& l : loop) { - ctr.appendUint16(static_cast(l.begin)); - ctr.appendUint16(static_cast(l.end)); - ctr.appendUint8(static_cast(l.times)); + auto loops = instMan.lock()->getOperatorSequenceFMLoopRoot(FM_OPSEQ_PARAMS[i], idx).getAllLoops(); + ctr.appendUint16(static_cast(loops.size())); + for (auto& loop : loops) { + ctr.appendUint16(static_cast(loop.getBeginPos())); + ctr.appendUint16(static_cast(loop.getEndPos())); + ctr.appendUint8(static_cast(loop.getTimes())); } auto release = instMan.lock()->getOperatorSequenceFMRelease(FM_OPSEQ_PARAMS[i], idx); - switch (release.type) { - case ReleaseType::NoRelease: + switch (release.getType()) { + case InstrumentSequenceRelease::NoRelease: ctr.appendUint8(0x00); break; - case ReleaseType::FixedRelease: + case InstrumentSequenceRelease::FixedRelease: ctr.appendUint8(0x01); - ctr.appendUint16(static_cast(release.begin)); + ctr.appendUint16(static_cast(release.getBeginPos())); break; - case ReleaseType::AbsoluteRelease: + case InstrumentSequenceRelease::AbsoluteRelease: ctr.appendUint8(0x02); - ctr.appendUint16(static_cast(release.begin)); + ctr.appendUint16(static_cast(release.getBeginPos())); break; - case ReleaseType::RelativeRelease: + case InstrumentSequenceRelease::RelativeRelease: ctr.appendUint8(0x03); - ctr.appendUint16(static_cast(release.begin)); + ctr.appendUint16(static_cast(release.getBeginPos())); break; } ctr.appendUint8(0); // Skip sequence type @@ -334,10 +338,8 @@ // FM arpeggio std::vector arpFMIdcs; for (auto& idx : instMan.lock()->getArpeggioFMEntriedIndices()) { - std::vector users = instMan.lock()->getArpeggioFMUsers(idx); - std::vector intersection; - std::set_intersection(users.begin(), users.end(), instNums.begin(), instNums.end(), std::back_inserter(intersection)); - if (!intersection.empty()) arpFMIdcs.push_back(idx); + if (judgeRequiredProperty(instMan.lock()->getArpeggioFMUsers(idx), instNums)) + arpFMIdcs.push_back(idx); } if (!arpFMIdcs.empty()) { ctr.appendUint8(0x28); @@ -348,39 +350,39 @@ ctr.appendUint16(0); // Dummy offset auto seq = instMan.lock()->getArpeggioFMSequence(idx); ctr.appendUint16(static_cast(seq.size())); - for (auto& com : seq) { - ctr.appendUint16(static_cast(com.type)); + for (auto& unit : seq) { + ctr.appendUint16(static_cast(unit.data)); } - auto loop = instMan.lock()->getArpeggioFMLoops(idx); - ctr.appendUint16(static_cast(loop.size())); - for (auto& l : loop) { - ctr.appendUint16(static_cast(l.begin)); - ctr.appendUint16(static_cast(l.end)); - ctr.appendUint8(static_cast(l.times)); + auto loops = instMan.lock()->getArpeggioFMLoopRoot(idx).getAllLoops(); + ctr.appendUint16(static_cast(loops.size())); + for (auto& loop : loops) { + ctr.appendUint16(static_cast(loop.getBeginPos())); + ctr.appendUint16(static_cast(loop.getEndPos())); + ctr.appendUint8(static_cast(loop.getTimes())); } auto release = instMan.lock()->getArpeggioFMRelease(idx); - switch (release.type) { - case ReleaseType::NoRelease: + switch (release.getType()) { + case InstrumentSequenceRelease::NoRelease: ctr.appendUint8(0x00); // If release.type is NO_RELEASE, then release.begin == -1 so omit to save it. break; - case ReleaseType::FixedRelease: + case InstrumentSequenceRelease::FixedRelease: ctr.appendUint8(0x01); - ctr.appendUint16(static_cast(release.begin)); + ctr.appendUint16(static_cast(release.getBeginPos())); break; - case ReleaseType::AbsoluteRelease: + case InstrumentSequenceRelease::AbsoluteRelease: ctr.appendUint8(0x02); - ctr.appendUint16(static_cast(release.begin)); + ctr.appendUint16(static_cast(release.getBeginPos())); break; - case ReleaseType::RelativeRelease: + case InstrumentSequenceRelease::RelativeRelease: ctr.appendUint8(0x03); - ctr.appendUint16(static_cast(release.begin)); + ctr.appendUint16(static_cast(release.getBeginPos())); break; } switch (instMan.lock()->getArpeggioFMType(idx)) { - case SequenceType::ABSOLUTE_SEQUENCE: ctr.appendUint8(0x00); break; - case SequenceType::FIXED_SEQUENCE: ctr.appendUint8(0x01); break; - case SequenceType::RELATIVE_SEQUENCE: ctr.appendUint8(0x02); break; + case SequenceType::AbsoluteSequence: ctr.appendUint8(0x00); break; + case SequenceType::FixedSequence: ctr.appendUint8(0x01); break; + case SequenceType::RelativeSequence: ctr.appendUint8(0x02); break; default: break; } ctr.writeUint16(ofs, static_cast(ctr.size() - ofs)); @@ -390,10 +392,8 @@ // FM pitch std::vector ptFMIdcs; for (auto& idx : instMan.lock()->getPitchFMEntriedIndices()) { - std::vector users = instMan.lock()->getPitchFMUsers(idx); - std::vector intersection; - std::set_intersection(users.begin(), users.end(), instNums.begin(), instNums.end(), std::back_inserter(intersection)); - if (!intersection.empty()) ptFMIdcs.push_back(idx); + if (judgeRequiredProperty(instMan.lock()->getPitchFMUsers(idx), instNums)) + ptFMIdcs.push_back(idx); } if (!ptFMIdcs.empty()) { ctr.appendUint8(0x29); @@ -404,38 +404,38 @@ ctr.appendUint16(0); // Dummy offset auto seq = instMan.lock()->getPitchFMSequence(idx); ctr.appendUint16(static_cast(seq.size())); - for (auto& com : seq) { - ctr.appendUint16(static_cast(com.type)); + for (auto& unit : seq) { + ctr.appendUint16(static_cast(unit.data)); } - auto loop = instMan.lock()->getPitchFMLoops(idx); - ctr.appendUint16(static_cast(loop.size())); - for (auto& l : loop) { - ctr.appendUint16(static_cast(l.begin)); - ctr.appendUint16(static_cast(l.end)); - ctr.appendUint8(static_cast(l.times)); + auto loops = instMan.lock()->getPitchFMLoopRoot(idx).getAllLoops(); + ctr.appendUint16(static_cast(loops.size())); + for (auto& loop : loops) { + ctr.appendUint16(static_cast(loop.getBeginPos())); + ctr.appendUint16(static_cast(loop.getEndPos())); + ctr.appendUint8(static_cast(loop.getTimes())); } auto release = instMan.lock()->getPitchFMRelease(idx); - switch (release.type) { - case ReleaseType::NoRelease: + switch (release.getType()) { + case InstrumentSequenceRelease::NoRelease: ctr.appendUint8(0x00); // If release.type is NO_RELEASE, then release.begin == -1 so omit to save it. break; - case ReleaseType::FixedRelease: + case InstrumentSequenceRelease::FixedRelease: ctr.appendUint8(0x01); - ctr.appendUint16(static_cast(release.begin)); + ctr.appendUint16(static_cast(release.getBeginPos())); break; - case ReleaseType::AbsoluteRelease: + case InstrumentSequenceRelease::AbsoluteRelease: ctr.appendUint8(0x02); - ctr.appendUint16(static_cast(release.begin)); + ctr.appendUint16(static_cast(release.getBeginPos())); break; - case ReleaseType::RelativeRelease: + case InstrumentSequenceRelease::RelativeRelease: ctr.appendUint8(0x03); - ctr.appendUint16(static_cast(release.begin)); + ctr.appendUint16(static_cast(release.getBeginPos())); break; } switch (instMan.lock()->getPitchFMType(idx)) { - case SequenceType::ABSOLUTE_SEQUENCE: ctr.appendUint8(0x00); break; - case SequenceType::RELATIVE_SEQUENCE: ctr.appendUint8(0x02); break; + case SequenceType::AbsoluteSequence: ctr.appendUint8(0x00); break; + case SequenceType::RelativeSequence: ctr.appendUint8(0x02); break; default: break; } ctr.writeUint16(ofs, static_cast(ctr.size() - ofs)); @@ -445,10 +445,8 @@ // SSG waveform std::vector wfSSGIdcs; for (auto& idx : instMan.lock()->getWaveformSSGEntriedIndices()) { - std::vector users = instMan.lock()->getWaveformSSGUsers(idx); - std::vector intersection; - std::set_intersection(users.begin(), users.end(), instNums.begin(), instNums.end(), std::back_inserter(intersection)); - if (!intersection.empty()) wfSSGIdcs.push_back(idx); + if (judgeRequiredProperty(instMan.lock()->getWaveformSSGUsers(idx), instNums)) + wfSSGIdcs.push_back(idx); } if (!wfSSGIdcs.empty()) { ctr.appendUint8(0x30); @@ -459,34 +457,34 @@ ctr.appendUint16(0); // Dummy offset auto seq = instMan.lock()->getWaveformSSGSequence(idx); ctr.appendUint16(static_cast(seq.size())); - for (auto& com : seq) { - ctr.appendUint16(static_cast(com.type)); - ctr.appendInt32(static_cast(com.data)); - } - auto loop = instMan.lock()->getWaveformSSGLoops(idx); - ctr.appendUint16(static_cast(loop.size())); - for (auto& l : loop) { - ctr.appendUint16(static_cast(l.begin)); - ctr.appendUint16(static_cast(l.end)); - ctr.appendUint8(static_cast(l.times)); + for (auto& unit : seq) { + ctr.appendUint16(static_cast(unit.data)); + ctr.appendInt32(static_cast(unit.subdata)); + } + auto loops = instMan.lock()->getWaveformSSGLoopRoot(idx).getAllLoops(); + ctr.appendUint16(static_cast(loops.size())); + for (auto& loop : loops) { + ctr.appendUint16(static_cast(loop.getBeginPos())); + ctr.appendUint16(static_cast(loop.getEndPos())); + ctr.appendUint8(static_cast(loop.getTimes())); } auto release = instMan.lock()->getWaveformSSGRelease(idx); - switch (release.type) { - case ReleaseType::NoRelease: + switch (release.getType()) { + case InstrumentSequenceRelease::NoRelease: ctr.appendUint8(0x00); // If release.type is NO_RELEASE, then release.begin == -1 so omit to save it. break; - case ReleaseType::FixedRelease: + case InstrumentSequenceRelease::FixedRelease: ctr.appendUint8(0x01); - ctr.appendUint16(static_cast(release.begin)); + ctr.appendUint16(static_cast(release.getBeginPos())); break; - case ReleaseType::AbsoluteRelease: + case InstrumentSequenceRelease::AbsoluteRelease: ctr.appendUint8(0x02); - ctr.appendUint16(static_cast(release.begin)); + ctr.appendUint16(static_cast(release.getBeginPos())); break; - case ReleaseType::RelativeRelease: + case InstrumentSequenceRelease::RelativeRelease: ctr.appendUint8(0x03); - ctr.appendUint16(static_cast(release.begin)); + ctr.appendUint16(static_cast(release.getBeginPos())); break; } ctr.appendUint8(0); // Skip sequence type @@ -497,10 +495,8 @@ // SSG tone/noise std::vector tnSSGIdcs; for (auto& idx : instMan.lock()->getToneNoiseSSGEntriedIndices()) { - std::vector users = instMan.lock()->getToneNoiseSSGUsers(idx); - std::vector intersection; - std::set_intersection(users.begin(), users.end(), instNums.begin(), instNums.end(), std::back_inserter(intersection)); - if (!intersection.empty()) tnSSGIdcs.push_back(idx); + if (judgeRequiredProperty(instMan.lock()->getToneNoiseSSGUsers(idx), instNums)) + tnSSGIdcs.push_back(idx); } if (!tnSSGIdcs.empty()) { ctr.appendUint8(0x31); @@ -511,33 +507,33 @@ ctr.appendUint16(0); // Dummy offset auto seq = instMan.lock()->getToneNoiseSSGSequence(idx); ctr.appendUint16(static_cast(seq.size())); - for (auto& com : seq) { - ctr.appendUint16(static_cast(com.type)); + for (auto& unit : seq) { + ctr.appendUint16(static_cast(unit.data)); } - auto loop = instMan.lock()->getToneNoiseSSGLoops(idx); - ctr.appendUint16(static_cast(loop.size())); - for (auto& l : loop) { - ctr.appendUint16(static_cast(l.begin)); - ctr.appendUint16(static_cast(l.end)); - ctr.appendUint8(static_cast(l.times)); + auto loops = instMan.lock()->getToneNoiseSSGLoopRoot(idx).getAllLoops(); + ctr.appendUint16(static_cast(loops.size())); + for (auto& loop : loops) { + ctr.appendUint16(static_cast(loop.getBeginPos())); + ctr.appendUint16(static_cast(loop.getEndPos())); + ctr.appendUint8(static_cast(loop.getTimes())); } auto release = instMan.lock()->getToneNoiseSSGRelease(idx); - switch (release.type) { - case ReleaseType::NoRelease: + switch (release.getType()) { + case InstrumentSequenceRelease::NoRelease: ctr.appendUint8(0x00); // If release.type is NO_RELEASE, then release.begin == -1 so omit to save it. break; - case ReleaseType::FixedRelease: + case InstrumentSequenceRelease::FixedRelease: ctr.appendUint8(0x01); - ctr.appendUint16(static_cast(release.begin)); + ctr.appendUint16(static_cast(release.getBeginPos())); break; - case ReleaseType::AbsoluteRelease: + case InstrumentSequenceRelease::AbsoluteRelease: ctr.appendUint8(0x02); - ctr.appendUint16(static_cast(release.begin)); + ctr.appendUint16(static_cast(release.getBeginPos())); break; - case ReleaseType::RelativeRelease: + case InstrumentSequenceRelease::RelativeRelease: ctr.appendUint8(0x03); - ctr.appendUint16(static_cast(release.begin)); + ctr.appendUint16(static_cast(release.getBeginPos())); break; } ctr.appendUint8(0); // Skip sequence type @@ -548,10 +544,8 @@ // SSG envelope std::vector envSSGIdcs; for (auto& idx : instMan.lock()->getEnvelopeSSGEntriedIndices()) { - std::vector users = instMan.lock()->getEnvelopeSSGUsers(idx); - std::vector intersection; - std::set_intersection(users.begin(), users.end(), instNums.begin(), instNums.end(), std::back_inserter(intersection)); - if (!intersection.empty()) envSSGIdcs.push_back(idx); + if (judgeRequiredProperty(instMan.lock()->getEnvelopeSSGUsers(idx), instNums)) + envSSGIdcs.push_back(idx); } if (!envSSGIdcs.empty()) { ctr.appendUint8(0x32); @@ -562,35 +556,35 @@ ctr.appendUint16(0); // Dummy offset auto seq = instMan.lock()->getEnvelopeSSGSequence(idx); ctr.appendUint16(static_cast(seq.size())); - for (auto& com : seq) { - ctr.appendUint16(static_cast(com.type)); - ctr.appendInt32(static_cast(com.data)); - } - auto loop = instMan.lock()->getEnvelopeSSGLoops(idx); - ctr.appendUint16(static_cast(loop.size())); - for (auto& l : loop) { - ctr.appendUint16(static_cast(l.begin)); - ctr.appendUint16(static_cast(l.end)); - ctr.appendUint8(static_cast(l.times)); + for (auto& unit : seq) { + ctr.appendUint16(static_cast(unit.data)); + ctr.appendInt32(static_cast(unit.subdata)); + } + auto loops = instMan.lock()->getEnvelopeSSGLoopRoot(idx).getAllLoops(); + ctr.appendUint16(static_cast(loops.size())); + for (auto& loop : loops) { + ctr.appendUint16(static_cast(loop.getBeginPos())); + ctr.appendUint16(static_cast(loop.getEndPos())); + ctr.appendUint8(static_cast(loop.getTimes())); } auto release = instMan.lock()->getEnvelopeSSGRelease(idx); - switch (release.type) { - case ReleaseType::NoRelease: + switch (release.getType()) { + case InstrumentSequenceRelease::NoRelease: ctr.appendUint8(0x00); // If release.type is NO_RELEASE, then release.begin == -1 so omit to save it. break; - case ReleaseType::FixedRelease: + case InstrumentSequenceRelease::FixedRelease: ctr.appendUint8(0x01); - ctr.appendUint16(static_cast(release.begin)); + ctr.appendUint16(static_cast(release.getBeginPos())); break; - case ReleaseType::AbsoluteRelease: + case InstrumentSequenceRelease::AbsoluteRelease: ctr.appendUint8(0x02); - ctr.appendUint16(static_cast(release.begin)); + ctr.appendUint16(static_cast(release.getBeginPos())); break; - case ReleaseType::RelativeRelease: + case InstrumentSequenceRelease::RelativeRelease: ctr.appendUint8(0x03); - ctr.appendUint16(static_cast(release.begin)); + ctr.appendUint16(static_cast(release.getBeginPos())); break; } ctr.appendUint8(0); // Skip sequence type @@ -601,10 +595,8 @@ // SSG arpeggio std::vector arpSSGIdcs; for (auto& idx : instMan.lock()->getArpeggioSSGEntriedIndices()) { - std::vector users = instMan.lock()->getArpeggioSSGUsers(idx); - std::vector intersection; - std::set_intersection(users.begin(), users.end(), instNums.begin(), instNums.end(), std::back_inserter(intersection)); - if (!intersection.empty()) arpSSGIdcs.push_back(idx); + if (judgeRequiredProperty(instMan.lock()->getArpeggioSSGUsers(idx), instNums)) + arpSSGIdcs.push_back(idx); } if (!arpSSGIdcs.empty()) { ctr.appendUint8(0x33); @@ -615,39 +607,39 @@ ctr.appendUint16(0); // Dummy offset auto seq = instMan.lock()->getArpeggioSSGSequence(idx); ctr.appendUint16(static_cast(seq.size())); - for (auto& com : seq) { - ctr.appendUint16(static_cast(com.type)); + for (auto& unit : seq) { + ctr.appendUint16(static_cast(unit.data)); } - auto loop = instMan.lock()->getArpeggioSSGLoops(idx); - ctr.appendUint16(static_cast(loop.size())); - for (auto& l : loop) { - ctr.appendUint16(static_cast(l.begin)); - ctr.appendUint16(static_cast(l.end)); - ctr.appendUint8(static_cast(l.times)); + auto loops = instMan.lock()->getArpeggioSSGLoopRoot(idx).getAllLoops(); + ctr.appendUint16(static_cast(loops.size())); + for (auto& loop : loops) { + ctr.appendUint16(static_cast(loop.getBeginPos())); + ctr.appendUint16(static_cast(loop.getEndPos())); + ctr.appendUint8(static_cast(loop.getTimes())); } auto release = instMan.lock()->getArpeggioSSGRelease(idx); - switch (release.type) { - case ReleaseType::NoRelease: + switch (release.getType()) { + case InstrumentSequenceRelease::NoRelease: ctr.appendUint8(0x00); // If release.type is NO_RELEASE, then release.begin == -1 so omit to save it. break; - case ReleaseType::FixedRelease: + case InstrumentSequenceRelease::FixedRelease: ctr.appendUint8(0x01); - ctr.appendUint16(static_cast(release.begin)); + ctr.appendUint16(static_cast(release.getBeginPos())); break; - case ReleaseType::AbsoluteRelease: + case InstrumentSequenceRelease::AbsoluteRelease: ctr.appendUint8(0x02); - ctr.appendUint16(static_cast(release.begin)); + ctr.appendUint16(static_cast(release.getBeginPos())); break; - case ReleaseType::RelativeRelease: + case InstrumentSequenceRelease::RelativeRelease: ctr.appendUint8(0x03); - ctr.appendUint16(static_cast(release.begin)); + ctr.appendUint16(static_cast(release.getBeginPos())); break; } switch (instMan.lock()->getArpeggioSSGType(idx)) { - case SequenceType::ABSOLUTE_SEQUENCE: ctr.appendUint8(0x00); break; - case SequenceType::FIXED_SEQUENCE: ctr.appendUint8(0x01); break; - case SequenceType::RELATIVE_SEQUENCE: ctr.appendUint8(0x02); break; + case SequenceType::AbsoluteSequence: ctr.appendUint8(0x00); break; + case SequenceType::FixedSequence: ctr.appendUint8(0x01); break; + case SequenceType::RelativeSequence: ctr.appendUint8(0x02); break; default: break; } ctr.writeUint16(ofs, static_cast(ctr.size() - ofs)); @@ -657,10 +649,8 @@ // SSG pitch std::vector ptSSGIdcs; for (auto& idx : instMan.lock()->getPitchSSGEntriedIndices()) { - std::vector users = instMan.lock()->getPitchSSGUsers(idx); - std::vector intersection; - std::set_intersection(users.begin(), users.end(), instNums.begin(), instNums.end(), std::back_inserter(intersection)); - if (!intersection.empty()) ptSSGIdcs.push_back(idx); + if (judgeRequiredProperty(instMan.lock()->getPitchSSGUsers(idx), instNums)) + ptSSGIdcs.push_back(idx); } if (!ptSSGIdcs.empty()) { ctr.appendUint8(0x34); @@ -671,38 +661,38 @@ ctr.appendUint16(0); // Dummy offset auto seq = instMan.lock()->getPitchSSGSequence(idx); ctr.appendUint16(static_cast(seq.size())); - for (auto& com : seq) { - ctr.appendUint16(static_cast(com.type)); + for (auto& unit : seq) { + ctr.appendUint16(static_cast(unit.data)); } - auto loop = instMan.lock()->getPitchSSGLoops(idx); - ctr.appendUint16(static_cast(loop.size())); - for (auto& l : loop) { - ctr.appendUint16(static_cast(l.begin)); - ctr.appendUint16(static_cast(l.end)); - ctr.appendUint8(static_cast(l.times)); + auto loops = instMan.lock()->getPitchSSGLoopRoot(idx).getAllLoops(); + ctr.appendUint16(static_cast(loops.size())); + for (auto& loop : loops) { + ctr.appendUint16(static_cast(loop.getBeginPos())); + ctr.appendUint16(static_cast(loop.getEndPos())); + ctr.appendUint8(static_cast(loop.getTimes())); } auto release = instMan.lock()->getPitchSSGRelease(idx); - switch (release.type) { - case ReleaseType::NoRelease: + switch (release.getType()) { + case InstrumentSequenceRelease::NoRelease: ctr.appendUint8(0x00); // If release.type is NO_RELEASE, then release.begin == -1 so omit to save it. break; - case ReleaseType::FixedRelease: + case InstrumentSequenceRelease::FixedRelease: ctr.appendUint8(0x01); - ctr.appendUint16(static_cast(release.begin)); + ctr.appendUint16(static_cast(release.getBeginPos())); break; - case ReleaseType::AbsoluteRelease: + case InstrumentSequenceRelease::AbsoluteRelease: ctr.appendUint8(0x02); - ctr.appendUint16(static_cast(release.begin)); + ctr.appendUint16(static_cast(release.getBeginPos())); break; - case ReleaseType::RelativeRelease: + case InstrumentSequenceRelease::RelativeRelease: ctr.appendUint8(0x03); - ctr.appendUint16(static_cast(release.begin)); + ctr.appendUint16(static_cast(release.getBeginPos())); break; } switch (instMan.lock()->getPitchSSGType(idx)) { - case SequenceType::ABSOLUTE_SEQUENCE: ctr.appendUint8(0x00); break; - case SequenceType::RELATIVE_SEQUENCE: ctr.appendUint8(0x02); break; + case SequenceType::AbsoluteSequence: ctr.appendUint8(0x00); break; + case SequenceType::RelativeSequence: ctr.appendUint8(0x02); break; default: break; } ctr.writeUint16(ofs, static_cast(ctr.size() - ofs)); @@ -712,10 +702,8 @@ // ADPCM sample std::vector sampADPCMIdcs; for (auto& idx : instMan.lock()->getSampleADPCMEntriedIndices()) { - std::vector users = instMan.lock()->getSampleADPCMUsers(idx); - std::vector intersection; - std::set_intersection(users.begin(), users.end(), instNums.begin(), instNums.end(), std::back_inserter(intersection)); - if (!intersection.empty()) sampADPCMIdcs.push_back(idx); + if (judgeRequiredProperty(instMan.lock()->getSampleADPCMUsers(idx), instNums)) + sampADPCMIdcs.push_back(idx); } if (!sampADPCMIdcs.empty()) { ctr.appendUint8(0x40); @@ -729,7 +717,7 @@ ctr.appendUint8(static_cast(instMan.lock()->isSampleADPCMRepeatable(idx))); std::vector samples = instMan.lock()->getSampleADPCMRawSample(idx); ctr.appendUint32(samples.size()); - ctr.appendVector(std::move(samples)); + ctr.appendVector(samples); ctr.writeUint32(ofs, ctr.size() - ofs); } } @@ -737,10 +725,8 @@ // ADPCM envelope std::vector envADPCMIdcs; for (auto& idx : instMan.lock()->getEnvelopeADPCMEntriedIndices()) { - std::vector users = instMan.lock()->getEnvelopeADPCMUsers(idx); - std::vector intersection; - std::set_intersection(users.begin(), users.end(), instNums.begin(), instNums.end(), std::back_inserter(intersection)); - if (!intersection.empty()) envADPCMIdcs.push_back(idx); + if (judgeRequiredProperty(instMan.lock()->getEnvelopeADPCMUsers(idx), instNums)) + envADPCMIdcs.push_back(idx); } if (!envADPCMIdcs.empty()) { ctr.appendUint8(0x41); @@ -751,35 +737,35 @@ ctr.appendUint16(0); // Dummy offset auto seq = instMan.lock()->getEnvelopeADPCMSequence(idx); ctr.appendUint16(static_cast(seq.size())); - for (auto& com : seq) { - ctr.appendUint16(static_cast(com.type)); - ctr.appendInt32(static_cast(com.data)); - } - auto loop = instMan.lock()->getEnvelopeADPCMLoops(idx); - ctr.appendUint16(static_cast(loop.size())); - for (auto& l : loop) { - ctr.appendUint16(static_cast(l.begin)); - ctr.appendUint16(static_cast(l.end)); - ctr.appendUint8(static_cast(l.times)); + for (auto& unit : seq) { + ctr.appendUint16(static_cast(unit.data)); + ctr.appendInt32(0); // Dummy set for past format + } + auto loops = instMan.lock()->getEnvelopeADPCMLoopRoot(idx).getAllLoops(); + ctr.appendUint16(static_cast(loops.size())); + for (auto& loop : loops) { + ctr.appendUint16(static_cast(loop.getBeginPos())); + ctr.appendUint16(static_cast(loop.getEndPos())); + ctr.appendUint8(static_cast(loop.getTimes())); } auto release = instMan.lock()->getEnvelopeADPCMRelease(idx); - switch (release.type) { - case ReleaseType::NoRelease: + switch (release.getType()) { + case InstrumentSequenceRelease::NoRelease: ctr.appendUint8(0x00); // If release.type is NO_RELEASE, then release.begin == -1 so omit to save it. break; - case ReleaseType::FixedRelease: + case InstrumentSequenceRelease::FixedRelease: ctr.appendUint8(0x01); - ctr.appendUint16(static_cast(release.begin)); + ctr.appendUint16(static_cast(release.getBeginPos())); break; - case ReleaseType::AbsoluteRelease: + case InstrumentSequenceRelease::AbsoluteRelease: ctr.appendUint8(0x02); - ctr.appendUint16(static_cast(release.begin)); + ctr.appendUint16(static_cast(release.getBeginPos())); break; - case ReleaseType::RelativeRelease: + case InstrumentSequenceRelease::RelativeRelease: ctr.appendUint8(0x03); - ctr.appendUint16(static_cast(release.begin)); + ctr.appendUint16(static_cast(release.getBeginPos())); break; } ctr.appendUint8(0); // Skip sequence type @@ -790,10 +776,8 @@ // ADPCM arpeggio std::vector arpADPCMIdcs; for (auto& idx : instMan.lock()->getArpeggioADPCMEntriedIndices()) { - std::vector users = instMan.lock()->getArpeggioADPCMUsers(idx); - std::vector intersection; - std::set_intersection(users.begin(), users.end(), instNums.begin(), instNums.end(), std::back_inserter(intersection)); - if (!intersection.empty()) arpADPCMIdcs.push_back(idx); + if (judgeRequiredProperty(instMan.lock()->getArpeggioADPCMUsers(idx), instNums)) + arpADPCMIdcs.push_back(idx); } if (!arpADPCMIdcs.empty()) { ctr.appendUint8(0x42); @@ -804,39 +788,39 @@ ctr.appendUint16(0); // Dummy offset auto seq = instMan.lock()->getArpeggioADPCMSequence(idx); ctr.appendUint16(static_cast(seq.size())); - for (auto& com : seq) { - ctr.appendUint16(static_cast(com.type)); + for (auto& unit : seq) { + ctr.appendUint16(static_cast(unit.data)); } - auto loop = instMan.lock()->getArpeggioADPCMLoops(idx); - ctr.appendUint16(static_cast(loop.size())); - for (auto& l : loop) { - ctr.appendUint16(static_cast(l.begin)); - ctr.appendUint16(static_cast(l.end)); - ctr.appendUint8(static_cast(l.times)); + auto loops = instMan.lock()->getArpeggioADPCMLoopRoot(idx).getAllLoops(); + ctr.appendUint16(static_cast(loops.size())); + for (auto& loop : loops) { + ctr.appendUint16(static_cast(loop.getBeginPos())); + ctr.appendUint16(static_cast(loop.getEndPos())); + ctr.appendUint8(static_cast(loop.getTimes())); } auto release = instMan.lock()->getArpeggioADPCMRelease(idx); - switch (release.type) { - case ReleaseType::NoRelease: + switch (release.getType()) { + case InstrumentSequenceRelease::NoRelease: ctr.appendUint8(0x00); // If release.type is NO_RELEASE, then release.begin == -1 so omit to save it. break; - case ReleaseType::FixedRelease: + case InstrumentSequenceRelease::FixedRelease: ctr.appendUint8(0x01); - ctr.appendUint16(static_cast(release.begin)); + ctr.appendUint16(static_cast(release.getBeginPos())); break; - case ReleaseType::AbsoluteRelease: + case InstrumentSequenceRelease::AbsoluteRelease: ctr.appendUint8(0x02); - ctr.appendUint16(static_cast(release.begin)); + ctr.appendUint16(static_cast(release.getBeginPos())); break; - case ReleaseType::RelativeRelease: + case InstrumentSequenceRelease::RelativeRelease: ctr.appendUint8(0x03); - ctr.appendUint16(static_cast(release.begin)); + ctr.appendUint16(static_cast(release.getBeginPos())); break; } switch (instMan.lock()->getArpeggioADPCMType(idx)) { - case SequenceType::ABSOLUTE_SEQUENCE: ctr.appendUint8(0x00); break; - case SequenceType::FIXED_SEQUENCE: ctr.appendUint8(0x01); break; - case SequenceType::RELATIVE_SEQUENCE: ctr.appendUint8(0x02); break; + case SequenceType::AbsoluteSequence: ctr.appendUint8(0x00); break; + case SequenceType::FixedSequence: ctr.appendUint8(0x01); break; + case SequenceType::RelativeSequence: ctr.appendUint8(0x02); break; default: break; } ctr.writeUint16(ofs, static_cast(ctr.size() - ofs)); @@ -846,10 +830,8 @@ // ADPCM pitch std::vector ptADPCMIdcs; for (auto& idx : instMan.lock()->getPitchADPCMEntriedIndices()) { - std::vector users = instMan.lock()->getPitchADPCMUsers(idx); - std::vector intersection; - std::set_intersection(users.begin(), users.end(), instNums.begin(), instNums.end(), std::back_inserter(intersection)); - if (!intersection.empty()) ptADPCMIdcs.push_back(idx); + if (judgeRequiredProperty(instMan.lock()->getPitchADPCMUsers(idx), instNums)) + ptADPCMIdcs.push_back(idx); } if (!ptADPCMIdcs.empty()) { ctr.appendUint8(0x43); @@ -860,38 +842,38 @@ ctr.appendUint16(0); // Dummy offset auto seq = instMan.lock()->getPitchADPCMSequence(idx); ctr.appendUint16(static_cast(seq.size())); - for (auto& com : seq) { - ctr.appendUint16(static_cast(com.type)); + for (auto& unit : seq) { + ctr.appendUint16(static_cast(unit.data)); } - auto loop = instMan.lock()->getPitchADPCMLoops(idx); - ctr.appendUint16(static_cast(loop.size())); - for (auto& l : loop) { - ctr.appendUint16(static_cast(l.begin)); - ctr.appendUint16(static_cast(l.end)); - ctr.appendUint8(static_cast(l.times)); + auto loops = instMan.lock()->getPitchADPCMLoopRoot(idx).getAllLoops(); + ctr.appendUint16(static_cast(loops.size())); + for (auto& loop : loops) { + ctr.appendUint16(static_cast(loop.getBeginPos())); + ctr.appendUint16(static_cast(loop.getEndPos())); + ctr.appendUint8(static_cast(loop.getTimes())); } auto release = instMan.lock()->getPitchADPCMRelease(idx); - switch (release.type) { - case ReleaseType::NoRelease: + switch (release.getType()) { + case InstrumentSequenceRelease::NoRelease: ctr.appendUint8(0x00); // If release.type is NO_RELEASE, then release.begin == -1 so omit to save it. break; - case ReleaseType::FixedRelease: + case InstrumentSequenceRelease::FixedRelease: ctr.appendUint8(0x01); - ctr.appendUint16(static_cast(release.begin)); + ctr.appendUint16(static_cast(release.getBeginPos())); break; - case ReleaseType::AbsoluteRelease: + case InstrumentSequenceRelease::AbsoluteRelease: ctr.appendUint8(0x02); - ctr.appendUint16(static_cast(release.begin)); + ctr.appendUint16(static_cast(release.getBeginPos())); break; - case ReleaseType::RelativeRelease: + case InstrumentSequenceRelease::RelativeRelease: ctr.appendUint8(0x03); - ctr.appendUint16(static_cast(release.begin)); + ctr.appendUint16(static_cast(release.getBeginPos())); break; } switch (instMan.lock()->getPitchADPCMType(idx)) { - case SequenceType::ABSOLUTE_SEQUENCE: ctr.appendUint8(0x00); break; - case SequenceType::RELATIVE_SEQUENCE: ctr.appendUint8(0x02); break; + case SequenceType::AbsoluteSequence: ctr.appendUint8(0x00); break; + case SequenceType::RelativeSequence: ctr.appendUint8(0x02); break; default: break; } ctr.writeUint16(ofs, static_cast(ctr.size() - ofs)); @@ -1076,28 +1058,25 @@ uint16_t data = propCtr.readUint16(opSeqCsr); opSeqCsr += 2; if (l == 0) - instManLocked->setOperatorSequenceFMSequenceCommand(param, opSeqNum, 0, data, 0); + instManLocked->setOperatorSequenceFMSequenceData(param, opSeqNum, 0, data); else - instManLocked->addOperatorSequenceFMSequenceCommand(param, opSeqNum, data, 0); + instManLocked->addOperatorSequenceFMSequenceData(param, opSeqNum, data); } uint16_t loopCnt = propCtr.readUint16(opSeqCsr); opSeqCsr += 2; - if (loopCnt > 0) { - std::vector begins, ends, times; - for (uint16_t l = 0; l < loopCnt; ++l) { - begins.push_back(propCtr.readUint16(opSeqCsr)); - opSeqCsr += 2; - ends.push_back(propCtr.readUint16(opSeqCsr)); - opSeqCsr += 2; - times.push_back(propCtr.readUint8(opSeqCsr++)); - } - instManLocked->setOperatorSequenceFMLoops(param, opSeqNum, begins, ends, times); + for (uint16_t l = 0; l < loopCnt; ++l) { + int begin = propCtr.readUint16(opSeqCsr); + opSeqCsr += 2; + int end = propCtr.readUint16(opSeqCsr); + opSeqCsr += 2; + int times = propCtr.readUint8(opSeqCsr++); + instManLocked->addOperatorSequenceFMLoop(param, opSeqNum, InstrumentSequenceLoop(begin, end, times)); } switch (propCtr.readUint8(opSeqCsr++)) { case 0x00: // No release - instManLocked->setOperatorSequenceFMRelease(param, opSeqNum, ReleaseType::NoRelease, -1); + instManLocked->setOperatorSequenceFMRelease(param, opSeqNum, InstrumentSequenceRelease(InstrumentSequenceRelease::NoRelease)); break; case 0x01: // Fixed { @@ -1105,8 +1084,8 @@ opSeqCsr += 2; // Release point check (prevents a bug) // https://github.com/rerrahkr/BambooTracker/issues/11 - if (pos < seqLen) instManLocked->setOperatorSequenceFMRelease(param, opSeqNum, ReleaseType::FixedRelease, pos); - else instManLocked->setOperatorSequenceFMRelease(param, opSeqNum, ReleaseType::NoRelease, -1); + if (pos < seqLen) instManLocked->setOperatorSequenceFMRelease(param, opSeqNum, InstrumentSequenceRelease(InstrumentSequenceRelease::FixedRelease, pos)); + else instManLocked->setOperatorSequenceFMRelease(param, opSeqNum, InstrumentSequenceRelease(InstrumentSequenceRelease::NoRelease)); break; } default: @@ -1150,28 +1129,25 @@ uint16_t data = propCtr.readUint16(arpCsr); arpCsr += 2; if (l == 0) - instManLocked->setArpeggioFMSequenceCommand(arpNum, 0, data, 0); + instManLocked->setArpeggioFMSequenceData(arpNum, 0, data); else - instManLocked->addArpeggioFMSequenceCommand(arpNum, data, 0); + instManLocked->addArpeggioFMSequenceData(arpNum, data); } uint16_t loopCnt = propCtr.readUint16(arpCsr); arpCsr += 2; - if (loopCnt > 0) { - std::vector begins, ends, times; - for (uint16_t l = 0; l < loopCnt; ++l) { - begins.push_back(propCtr.readUint16(arpCsr)); - arpCsr += 2; - ends.push_back(propCtr.readUint16(arpCsr)); - arpCsr += 2; - times.push_back(propCtr.readUint8(arpCsr++)); - } - instManLocked->setArpeggioFMLoops(arpNum, begins, ends, times); + for (uint16_t l = 0; l < loopCnt; ++l) { + int begin = propCtr.readUint16(arpCsr); + arpCsr += 2; + int end = propCtr.readUint16(arpCsr); + arpCsr += 2; + int times = propCtr.readUint8(arpCsr++); + instManLocked->addArpeggioFMLoop(arpNum, InstrumentSequenceLoop(begin, end, times)); } switch (propCtr.readUint8(arpCsr++)) { case 0x00: // No release - instManLocked->setArpeggioFMRelease(arpNum, ReleaseType::NoRelease, -1); + instManLocked->setArpeggioFMRelease(arpNum, InstrumentSequenceRelease(InstrumentSequenceRelease::NoRelease)); break; case 0x01: // Fixed { @@ -1179,8 +1155,8 @@ arpCsr += 2; // Release point check (prevents a bug) // https://github.com/rerrahkr/BambooTracker/issues/11 - if (pos < seqLen) instManLocked->setArpeggioFMRelease(arpNum, ReleaseType::FixedRelease, pos); - else instManLocked->setArpeggioFMRelease(arpNum, ReleaseType::NoRelease, -1); + if (pos < seqLen) instManLocked->setArpeggioFMRelease(arpNum, InstrumentSequenceRelease(InstrumentSequenceRelease::FixedRelease, pos)); + else instManLocked->setArpeggioFMRelease(arpNum, InstrumentSequenceRelease(InstrumentSequenceRelease::NoRelease)); break; } default: @@ -1189,19 +1165,19 @@ switch (propCtr.readUint8(arpCsr++)) { case 0x00: // Absolute - instManLocked->setArpeggioFMType(arpNum, SequenceType::ABSOLUTE_SEQUENCE); + instManLocked->setArpeggioFMType(arpNum, SequenceType::AbsoluteSequence); break; case 0x01: // Fixed - instManLocked->setArpeggioFMType(arpNum, SequenceType::FIXED_SEQUENCE); + instManLocked->setArpeggioFMType(arpNum, SequenceType::FixedSequence); break; case 0x02: // Relative - instManLocked->setArpeggioFMType(arpNum, SequenceType::RELATIVE_SEQUENCE); + instManLocked->setArpeggioFMType(arpNum, SequenceType::RelativeSequence); break; default: if (bankVersion < Version::toBCD(1, 0, 2)) { // Recover deep clone bug // https://github.com/rerrahkr/BambooTracker/issues/170 - instManLocked->setArpeggioFMType(arpNum, SequenceType::ABSOLUTE_SEQUENCE); + instManLocked->setArpeggioFMType(arpNum, SequenceType::AbsoluteSequence); break; } else { @@ -1251,28 +1227,25 @@ uint16_t data = propCtr.readUint16(ptCsr); ptCsr += 2; if (l == 0) - instManLocked->setPitchFMSequenceCommand(ptNum, 0, data, 0); + instManLocked->setPitchFMSequenceData(ptNum, 0, data); else - instManLocked->addPitchFMSequenceCommand(ptNum, data, 0); + instManLocked->addPitchFMSequenceData(ptNum, data); } uint16_t loopCnt = propCtr.readUint16(ptCsr); ptCsr += 2; - if (loopCnt > 0) { - std::vector begins, ends, times; - for (uint16_t l = 0; l < loopCnt; ++l) { - begins.push_back(propCtr.readUint16(ptCsr)); - ptCsr += 2; - ends.push_back(propCtr.readUint16(ptCsr)); - ptCsr += 2; - times.push_back(propCtr.readUint8(ptCsr++)); - } - instManLocked->setPitchFMLoops(ptNum, begins, ends, times); + for (uint16_t l = 0; l < loopCnt; ++l) { + int begin = propCtr.readUint16(ptCsr); + ptCsr += 2; + int end = propCtr.readUint16(ptCsr); + ptCsr += 2; + int times = propCtr.readUint8(ptCsr++); + instManLocked->addPitchFMLoop(ptNum, InstrumentSequenceLoop(begin, end, times)); } switch (propCtr.readUint8(ptCsr++)) { case 0x00: // No release - instManLocked->setPitchFMRelease(ptNum, ReleaseType::NoRelease, -1); + instManLocked->setPitchFMRelease(ptNum, InstrumentSequenceRelease(InstrumentSequenceRelease::NoRelease)); break; case 0x01: // Fixed { @@ -1280,8 +1253,8 @@ ptCsr += 2; // Release point check (prevents a bug) // https://github.com/rerrahkr/BambooTracker/issues/11 - if (pos < seqLen) instManLocked->setPitchFMRelease(ptNum, ReleaseType::FixedRelease, pos); - else instManLocked->setPitchFMRelease(ptNum, ReleaseType::NoRelease, -1); + if (pos < seqLen) instManLocked->setPitchFMRelease(ptNum, InstrumentSequenceRelease(InstrumentSequenceRelease::FixedRelease, pos)); + else instManLocked->setPitchFMRelease(ptNum, InstrumentSequenceRelease(InstrumentSequenceRelease::NoRelease)); break; } default: @@ -1290,16 +1263,16 @@ switch (propCtr.readUint8(ptCsr++)) { case 0x00: // Absolute - instManLocked->setPitchFMType(ptNum, SequenceType::ABSOLUTE_SEQUENCE); + instManLocked->setPitchFMType(ptNum, SequenceType::AbsoluteSequence); break; case 0x02: // Relative - instManLocked->setPitchFMType(ptNum, SequenceType::RELATIVE_SEQUENCE); + instManLocked->setPitchFMType(ptNum, SequenceType::RelativeSequence); break; default: if (bankVersion < Version::toBCD(1, 0, 2)) { // Recover deep clone bug // https://github.com/rerrahkr/BambooTracker/issues/170 - instManLocked->setPitchFMType(ptNum, SequenceType::ABSOLUTE_SEQUENCE); + instManLocked->setPitchFMType(ptNum, SequenceType::AbsoluteSequence); break; } else { @@ -1355,29 +1328,37 @@ int32_t subdata; subdata = propCtr.readInt32(wfCsr); wfCsr += 4; + SSGWaveformUnit unit; + switch (data) { + case SSGWaveformType::SQM_TRIANGLE: + case SSGWaveformType::SQM_SAW: + case SSGWaveformType::SQM_INVSAW: + unit = SSGWaveformUnit::makeUnitWithDecode(data, subdata); + break; + default: + unit = SSGWaveformUnit::makeOnlyDataUnit(data); + break; + } if (l == 0) - instManLocked->setWaveformSSGSequenceCommand(wfNum, 0, data, subdata); + instManLocked->setWaveformSSGSequenceData(wfNum, 0, unit); else - instManLocked->addWaveformSSGSequenceCommand(wfNum, data, subdata); + instManLocked->addWaveformSSGSequenceData(wfNum, unit); } uint16_t loopCnt = propCtr.readUint16(wfCsr); wfCsr += 2; - if (loopCnt > 0) { - std::vector begins, ends, times; - for (uint16_t l = 0; l < loopCnt; ++l) { - begins.push_back(propCtr.readUint16(wfCsr)); - wfCsr += 2; - ends.push_back(propCtr.readUint16(wfCsr)); - wfCsr += 2; - times.push_back(propCtr.readUint8(wfCsr++)); - } - instManLocked->setWaveformSSGLoops(wfNum, begins, ends, times); + for (uint16_t l = 0; l < loopCnt; ++l) { + int begin = propCtr.readUint16(wfCsr); + wfCsr += 2; + int end = propCtr.readUint16(wfCsr); + wfCsr += 2; + int times = propCtr.readUint8(wfCsr++); + instManLocked->addWaveformSSGLoop(wfNum, InstrumentSequenceLoop(begin, end, times)); } switch (propCtr.readUint8(wfCsr++)) { case 0x00: // No release - instManLocked->setWaveformSSGRelease(wfNum, ReleaseType::NoRelease, -1); + instManLocked->setWaveformSSGRelease(wfNum, InstrumentSequenceRelease(InstrumentSequenceRelease::NoRelease)); break; case 0x01: // Fixed { @@ -1385,8 +1366,8 @@ wfCsr += 2; // Release point check (prevents a bug) // https://github.com/rerrahkr/BambooTracker/issues/11 - if (pos < seqLen) instManLocked->setWaveformSSGRelease(wfNum, ReleaseType::FixedRelease, pos); - else instManLocked->setWaveformSSGRelease(wfNum, ReleaseType::NoRelease, -1); + if (pos < seqLen) instManLocked->setWaveformSSGRelease(wfNum, InstrumentSequenceRelease(InstrumentSequenceRelease::FixedRelease, pos)); + else instManLocked->setWaveformSSGRelease(wfNum, InstrumentSequenceRelease(InstrumentSequenceRelease::NoRelease)); break; } default: @@ -1424,28 +1405,25 @@ } } if (l == 0) - instManLocked->setToneNoiseSSGSequenceCommand(tnNum, 0, data, 0); + instManLocked->setToneNoiseSSGSequenceData(tnNum, 0, data); else - instManLocked->addToneNoiseSSGSequenceCommand(tnNum, data, 0); + instManLocked->addToneNoiseSSGSequenceData(tnNum, data); } uint16_t loopCnt = propCtr.readUint16(tnCsr); tnCsr += 2; - if (loopCnt > 0) { - std::vector begins, ends, times; - for (uint16_t l = 0; l < loopCnt; ++l) { - begins.push_back(propCtr.readUint16(tnCsr)); - tnCsr += 2; - ends.push_back(propCtr.readUint16(tnCsr)); - tnCsr += 2; - times.push_back(propCtr.readUint8(tnCsr++)); - } - instManLocked->setToneNoiseSSGLoops(tnNum, begins, ends, times); + for (uint16_t l = 0; l < loopCnt; ++l) { + int begin = propCtr.readUint16(tnCsr); + tnCsr += 2; + int end = propCtr.readUint16(tnCsr); + tnCsr += 2; + int times = propCtr.readUint8(tnCsr++); + instManLocked->addToneNoiseSSGLoop(tnNum, InstrumentSequenceLoop(begin, end, times)); } switch (propCtr.readUint8(tnCsr++)) { case 0x00: // No release - instManLocked->setToneNoiseSSGRelease(tnNum, ReleaseType::NoRelease, -1); + instManLocked->setToneNoiseSSGRelease(tnNum, InstrumentSequenceRelease(InstrumentSequenceRelease::NoRelease)); break; case 0x01: // Fixed { @@ -1453,8 +1431,8 @@ tnCsr += 2; // Release point check (prevents a bug) // https://github.com/rerrahkr/BambooTracker/issues/11 - if (pos < seqLen) instManLocked->setToneNoiseSSGRelease(tnNum, ReleaseType::FixedRelease, pos); - else instManLocked->setToneNoiseSSGRelease(tnNum, ReleaseType::NoRelease, -1); + if (pos < seqLen) instManLocked->setToneNoiseSSGRelease(tnNum, InstrumentSequenceRelease(InstrumentSequenceRelease::FixedRelease, pos)); + else instManLocked->setToneNoiseSSGRelease(tnNum, InstrumentSequenceRelease(InstrumentSequenceRelease::NoRelease)); break; } default: @@ -1488,29 +1466,28 @@ int32_t subdata; subdata = propCtr.readInt32(envCsr); envCsr += 4; + SSGEnvelopeUnit unit = (data < 16) ? SSGEnvelopeUnit::makeOnlyDataUnit(data) + : SSGEnvelopeUnit::makeUnitWithDecode(data, subdata); if (l == 0) - instManLocked->setEnvelopeSSGSequenceCommand(envNum, 0, data, subdata); + instManLocked->setEnvelopeSSGSequenceData(envNum, 0, unit); else - instManLocked->addEnvelopeSSGSequenceCommand(envNum, data, subdata); + instManLocked->addEnvelopeSSGSequenceData(envNum, unit); } uint16_t loopCnt = propCtr.readUint16(envCsr); envCsr += 2; - if (loopCnt > 0) { - std::vector begins, ends, times; - for (uint16_t l = 0; l < loopCnt; ++l) { - begins.push_back(propCtr.readUint16(envCsr)); - envCsr += 2; - ends.push_back(propCtr.readUint16(envCsr)); - envCsr += 2; - times.push_back(propCtr.readUint8(envCsr++)); - } - instManLocked->setEnvelopeSSGLoops(envNum, begins, ends, times); + for (uint16_t l = 0; l < loopCnt; ++l) { + int begin = propCtr.readUint16(envCsr); + envCsr += 2; + int end = propCtr.readUint16(envCsr); + envCsr += 2; + int times = propCtr.readUint8(envCsr++); + instManLocked->addEnvelopeSSGLoop(envNum, InstrumentSequenceLoop(begin, end, times)); } switch (propCtr.readUint8(envCsr++)) { case 0x00: // No release - instManLocked->setEnvelopeSSGRelease(envNum, ReleaseType::NoRelease, -1); + instManLocked->setEnvelopeSSGRelease(envNum, InstrumentSequenceRelease(InstrumentSequenceRelease::NoRelease)); break; // Release point check (prevents a bug) // https://github.com/rerrahkr/BambooTracker/issues/11 @@ -1518,24 +1495,24 @@ { uint16_t pos = propCtr.readUint16(envCsr); envCsr += 2; - if (pos < seqLen) instManLocked->setEnvelopeSSGRelease(envNum, ReleaseType::FixedRelease, pos); - else instManLocked->setEnvelopeSSGRelease(envNum, ReleaseType::NoRelease, -1); + if (pos < seqLen) instManLocked->setEnvelopeSSGRelease(envNum, InstrumentSequenceRelease(InstrumentSequenceRelease::FixedRelease, pos)); + else instManLocked->setEnvelopeSSGRelease(envNum, InstrumentSequenceRelease(InstrumentSequenceRelease::NoRelease)); break; } case 0x02: // Absolute { uint16_t pos = propCtr.readUint16(envCsr); envCsr += 2; - if (pos < seqLen) instManLocked->setEnvelopeSSGRelease(envNum, ReleaseType::AbsoluteRelease, pos); - else instManLocked->setEnvelopeSSGRelease(envNum, ReleaseType::NoRelease, -1); + if (pos < seqLen) instManLocked->setEnvelopeSSGRelease(envNum, InstrumentSequenceRelease(InstrumentSequenceRelease::AbsoluteRelease, pos)); + else instManLocked->setEnvelopeSSGRelease(envNum, InstrumentSequenceRelease(InstrumentSequenceRelease::NoRelease)); break; } case 0x03: // Relative { uint16_t pos = propCtr.readUint16(envCsr); envCsr += 2; - if (pos < seqLen) instManLocked->setEnvelopeSSGRelease(envNum, ReleaseType::RelativeRelease, pos); - else instManLocked->setEnvelopeSSGRelease(envNum, ReleaseType::NoRelease, -1); + if (pos < seqLen) instManLocked->setEnvelopeSSGRelease(envNum, InstrumentSequenceRelease(InstrumentSequenceRelease::RelativeRelease, pos)); + else instManLocked->setEnvelopeSSGRelease(envNum, InstrumentSequenceRelease(InstrumentSequenceRelease::NoRelease)); break; } default: @@ -1567,28 +1544,25 @@ uint16_t data = propCtr.readUint16(arpCsr); arpCsr += 2; if (l == 0) - instManLocked->setArpeggioSSGSequenceCommand(arpNum, 0, data, 0); + instManLocked->setArpeggioSSGSequenceData(arpNum, 0, data); else - instManLocked->addArpeggioSSGSequenceCommand(arpNum, data, 0); + instManLocked->addArpeggioSSGSequenceData(arpNum, data); } uint16_t loopCnt = propCtr.readUint16(arpCsr); arpCsr += 2; - if (loopCnt > 0) { - std::vector begins, ends, times; - for (uint16_t l = 0; l < loopCnt; ++l) { - begins.push_back(propCtr.readUint16(arpCsr)); - arpCsr += 2; - ends.push_back(propCtr.readUint16(arpCsr)); - arpCsr += 2; - times.push_back(propCtr.readUint8(arpCsr++)); - } - instManLocked->setArpeggioSSGLoops(arpNum, begins, ends, times); + for (uint16_t l = 0; l < loopCnt; ++l) { + int begin = propCtr.readUint16(arpCsr); + arpCsr += 2; + int end = propCtr.readUint16(arpCsr); + arpCsr += 2; + int times = propCtr.readUint8(arpCsr++); + instManLocked->addArpeggioSSGLoop(arpNum, InstrumentSequenceLoop(begin, end, times)); } switch (propCtr.readUint8(arpCsr++)) { case 0x00: // No release - instManLocked->setArpeggioSSGRelease(arpNum, ReleaseType::NoRelease, -1); + instManLocked->setArpeggioSSGRelease(arpNum, InstrumentSequenceRelease(InstrumentSequenceRelease::NoRelease)); break; case 0x01: // Fixed { @@ -1596,8 +1570,8 @@ arpCsr += 2; // Release point check (prevents a bug) // https://github.com/rerrahkr/BambooTracker/issues/11 - if (pos < seqLen) instManLocked->setArpeggioSSGRelease(arpNum, ReleaseType::FixedRelease, pos); - else instManLocked->setArpeggioSSGRelease(arpNum, ReleaseType::NoRelease, -1); + if (pos < seqLen) instManLocked->setArpeggioSSGRelease(arpNum, InstrumentSequenceRelease(InstrumentSequenceRelease::FixedRelease, pos)); + else instManLocked->setArpeggioSSGRelease(arpNum, InstrumentSequenceRelease(InstrumentSequenceRelease::NoRelease)); break; } default: @@ -1606,19 +1580,19 @@ switch (propCtr.readUint8(arpCsr++)) { case 0x00: // Absolute - instManLocked->setArpeggioSSGType(arpNum, SequenceType::ABSOLUTE_SEQUENCE); + instManLocked->setArpeggioSSGType(arpNum, SequenceType::AbsoluteSequence); break; case 0x01: // Fixed - instManLocked->setArpeggioSSGType(arpNum, SequenceType::FIXED_SEQUENCE); + instManLocked->setArpeggioSSGType(arpNum, SequenceType::FixedSequence); break; case 0x02: // Relative - instManLocked->setArpeggioSSGType(arpNum, SequenceType::RELATIVE_SEQUENCE); + instManLocked->setArpeggioSSGType(arpNum, SequenceType::RelativeSequence); break; default: if (bankVersion < Version::toBCD(1, 0, 2)) { // Recover deep clone bug // https://github.com/rerrahkr/BambooTracker/issues/170 - instManLocked->setArpeggioSSGType(arpNum, SequenceType::ABSOLUTE_SEQUENCE); + instManLocked->setArpeggioSSGType(arpNum, SequenceType::AbsoluteSequence); break; } else { @@ -1651,28 +1625,25 @@ uint16_t data = propCtr.readUint16(ptCsr); ptCsr += 2; if (l == 0) - instManLocked->setPitchSSGSequenceCommand(ptNum, 0, data, 0); + instManLocked->setPitchSSGSequenceData(ptNum, 0, data); else - instManLocked->addPitchSSGSequenceCommand(ptNum, data, 0); + instManLocked->addPitchSSGSequenceData(ptNum, data); } uint16_t loopCnt = propCtr.readUint16(ptCsr); ptCsr += 2; - if (loopCnt > 0) { - std::vector begins, ends, times; - for (uint16_t l = 0; l < loopCnt; ++l) { - begins.push_back(propCtr.readUint16(ptCsr)); - ptCsr += 2; - ends.push_back(propCtr.readUint16(ptCsr)); - ptCsr += 2; - times.push_back(propCtr.readUint8(ptCsr++)); - } - instManLocked->setPitchSSGLoops(ptNum, begins, ends, times); + for (uint16_t l = 0; l < loopCnt; ++l) { + int begin = propCtr.readUint16(ptCsr); + ptCsr += 2; + int end = propCtr.readUint16(ptCsr); + ptCsr += 2; + int times = propCtr.readUint8(ptCsr++); + instManLocked->addPitchSSGLoop(ptNum, InstrumentSequenceLoop(begin, end, times)); } switch (propCtr.readUint8(ptCsr++)) { case 0x00: // No release - instManLocked->setPitchSSGRelease(ptNum, ReleaseType::NoRelease, -1); + instManLocked->setPitchSSGRelease(ptNum, InstrumentSequenceRelease(InstrumentSequenceRelease::NoRelease)); break; case 0x01: // Fixed { @@ -1680,8 +1651,8 @@ ptCsr += 2; // Release point check (prevents a bug) // https://github.com/rerrahkr/BambooTracker/issues/11 - if (pos < seqLen) instManLocked->setPitchSSGRelease(ptNum, ReleaseType::FixedRelease, pos); - else instManLocked->setPitchSSGRelease(ptNum, ReleaseType::NoRelease, -1); + if (pos < seqLen) instManLocked->setPitchSSGRelease(ptNum, InstrumentSequenceRelease(InstrumentSequenceRelease::FixedRelease, pos)); + else instManLocked->setPitchSSGRelease(ptNum, InstrumentSequenceRelease(InstrumentSequenceRelease::NoRelease)); break; } default: @@ -1690,16 +1661,16 @@ switch (propCtr.readUint8(ptCsr++)) { case 0x00: // Absolute - instManLocked->setPitchSSGType(ptNum, SequenceType::ABSOLUTE_SEQUENCE); + instManLocked->setPitchSSGType(ptNum, SequenceType::AbsoluteSequence); break; case 0x02: // Relative - instManLocked->setPitchSSGType(ptNum, SequenceType::RELATIVE_SEQUENCE); + instManLocked->setPitchSSGType(ptNum, SequenceType::RelativeSequence); break; default: if (bankVersion < Version::toBCD(1, 0, 2)) { // Recover deep clone bug // https://github.com/rerrahkr/BambooTracker/issues/170 - instManLocked->setPitchSSGType(ptNum, SequenceType::ABSOLUTE_SEQUENCE); + instManLocked->setPitchSSGType(ptNum, SequenceType::AbsoluteSequence); break; } else { @@ -1756,33 +1727,27 @@ envCsr += 2; for (uint16_t l = 0; l < seqLen; ++l) { uint16_t data = propCtr.readUint16(envCsr); - envCsr += 2; - int32_t subdata; - subdata = propCtr.readInt32(envCsr); - envCsr += 4; + envCsr += 6; // Skip subdata if (l == 0) - instManLocked->setEnvelopeADPCMSequenceCommand(envNum, 0, data, subdata); + instManLocked->setEnvelopeADPCMSequenceData(envNum, 0, data); else - instManLocked->addEnvelopeADPCMSequenceCommand(envNum, data, subdata); + instManLocked->addEnvelopeADPCMSequenceData(envNum, data); } uint16_t loopCnt = propCtr.readUint16(envCsr); envCsr += 2; - if (loopCnt > 0) { - std::vector begins, ends, times; - for (uint16_t l = 0; l < loopCnt; ++l) { - begins.push_back(propCtr.readUint16(envCsr)); - envCsr += 2; - ends.push_back(propCtr.readUint16(envCsr)); - envCsr += 2; - times.push_back(propCtr.readUint8(envCsr++)); - } - instManLocked->setEnvelopeADPCMLoops(envNum, begins, ends, times); + for (uint16_t l = 0; l < loopCnt; ++l) { + int begin = propCtr.readUint16(envCsr); + envCsr += 2; + int end = propCtr.readUint16(envCsr); + envCsr += 2; + int times = propCtr.readUint8(envCsr++); + instManLocked->addEnvelopeADPCMLoop(envNum, InstrumentSequenceLoop(begin, end, times)); } switch (propCtr.readUint8(envCsr++)) { case 0x00: // No release - instManLocked->setEnvelopeADPCMRelease(envNum, ReleaseType::NoRelease, -1); + instManLocked->setEnvelopeADPCMRelease(envNum, InstrumentSequenceRelease(InstrumentSequenceRelease::NoRelease)); break; // Release point check (prevents a bug) // https://github.com/rerrahkr/BambooTracker/issues/11 @@ -1790,24 +1755,24 @@ { uint16_t pos = propCtr.readUint16(envCsr); envCsr += 2; - if (pos < seqLen) instManLocked->setEnvelopeADPCMRelease(envNum, ReleaseType::FixedRelease, pos); - else instManLocked->setEnvelopeADPCMRelease(envNum, ReleaseType::NoRelease, -1); + if (pos < seqLen) instManLocked->setEnvelopeADPCMRelease(envNum, InstrumentSequenceRelease(InstrumentSequenceRelease::FixedRelease, pos)); + else instManLocked->setEnvelopeADPCMRelease(envNum, InstrumentSequenceRelease(InstrumentSequenceRelease::NoRelease)); break; } case 0x02: // Absolute { uint16_t pos = propCtr.readUint16(envCsr); envCsr += 2; - if (pos < seqLen) instManLocked->setEnvelopeADPCMRelease(envNum, ReleaseType::AbsoluteRelease, pos); - else instManLocked->setEnvelopeADPCMRelease(envNum, ReleaseType::NoRelease, -1); + if (pos < seqLen) instManLocked->setEnvelopeADPCMRelease(envNum, InstrumentSequenceRelease(InstrumentSequenceRelease::AbsoluteRelease, pos)); + else instManLocked->setEnvelopeADPCMRelease(envNum, InstrumentSequenceRelease(InstrumentSequenceRelease::NoRelease)); break; } case 0x03: // Relative { uint16_t pos = propCtr.readUint16(envCsr); envCsr += 2; - if (pos < seqLen) instManLocked->setEnvelopeADPCMRelease(envNum, ReleaseType::RelativeRelease, pos); - else instManLocked->setEnvelopeADPCMRelease(envNum, ReleaseType::NoRelease, -1); + if (pos < seqLen) instManLocked->setEnvelopeADPCMRelease(envNum, InstrumentSequenceRelease(InstrumentSequenceRelease::RelativeRelease, pos)); + else instManLocked->setEnvelopeADPCMRelease(envNum, InstrumentSequenceRelease(InstrumentSequenceRelease::NoRelease)); break; } default: @@ -1839,28 +1804,25 @@ uint16_t data = propCtr.readUint16(arpCsr); arpCsr += 2; if (l == 0) - instManLocked->setArpeggioADPCMSequenceCommand(arpNum, 0, data, 0); + instManLocked->setArpeggioADPCMSequenceData(arpNum, 0, data); else - instManLocked->addArpeggioADPCMSequenceCommand(arpNum, data, 0); + instManLocked->addArpeggioADPCMSequenceData(arpNum, data); } uint16_t loopCnt = propCtr.readUint16(arpCsr); arpCsr += 2; - if (loopCnt > 0) { - std::vector begins, ends, times; - for (uint16_t l = 0; l < loopCnt; ++l) { - begins.push_back(propCtr.readUint16(arpCsr)); - arpCsr += 2; - ends.push_back(propCtr.readUint16(arpCsr)); - arpCsr += 2; - times.push_back(propCtr.readUint8(arpCsr++)); - } - instManLocked->setArpeggioADPCMLoops(arpNum, begins, ends, times); + for (uint16_t l = 0; l < loopCnt; ++l) { + int begin = propCtr.readUint16(arpCsr); + arpCsr += 2; + int end = propCtr.readUint16(arpCsr); + arpCsr += 2; + int times = propCtr.readUint8(arpCsr++); + instManLocked->addArpeggioADPCMLoop(arpNum, InstrumentSequenceLoop(begin, end, times)); } switch (propCtr.readUint8(arpCsr++)) { case 0x00: // No release - instManLocked->setArpeggioADPCMRelease(arpNum, ReleaseType::NoRelease, -1); + instManLocked->setArpeggioADPCMRelease(arpNum, InstrumentSequenceRelease(InstrumentSequenceRelease::NoRelease)); break; case 0x01: // Fixed { @@ -1868,8 +1830,8 @@ arpCsr += 2; // Release point check (prevents a bug) // https://github.com/rerrahkr/BambooTracker/issues/11 - if (pos < seqLen) instManLocked->setArpeggioADPCMRelease(arpNum, ReleaseType::FixedRelease, pos); - else instManLocked->setArpeggioADPCMRelease(arpNum, ReleaseType::NoRelease, -1); + if (pos < seqLen) instManLocked->setArpeggioADPCMRelease(arpNum, InstrumentSequenceRelease(InstrumentSequenceRelease::FixedRelease, pos)); + else instManLocked->setArpeggioADPCMRelease(arpNum, InstrumentSequenceRelease(InstrumentSequenceRelease::NoRelease)); break; } default: @@ -1878,13 +1840,13 @@ switch (propCtr.readUint8(arpCsr++)) { case 0x00: // Absolute - instManLocked->setArpeggioADPCMType(arpNum, SequenceType::ABSOLUTE_SEQUENCE); + instManLocked->setArpeggioADPCMType(arpNum, SequenceType::AbsoluteSequence); break; case 0x01: // Fixed - instManLocked->setArpeggioADPCMType(arpNum, SequenceType::FIXED_SEQUENCE); + instManLocked->setArpeggioADPCMType(arpNum, SequenceType::FixedSequence); break; case 0x02: // Relative - instManLocked->setArpeggioADPCMType(arpNum, SequenceType::RELATIVE_SEQUENCE); + instManLocked->setArpeggioADPCMType(arpNum, SequenceType::RelativeSequence); break; default: throw FileCorruptionError(FileType::Bank, arpCsr); @@ -1915,28 +1877,25 @@ uint16_t data = propCtr.readUint16(ptCsr); ptCsr += 2; if (l == 0) - instManLocked->setPitchADPCMSequenceCommand(ptNum, 0, data, 0); + instManLocked->setPitchADPCMSequenceData(ptNum, 0, data); else - instManLocked->addPitchADPCMSequenceCommand(ptNum, data, 0); + instManLocked->addPitchADPCMSequenceData(ptNum, data); } uint16_t loopCnt = propCtr.readUint16(ptCsr); ptCsr += 2; - if (loopCnt > 0) { - std::vector begins, ends, times; - for (uint16_t l = 0; l < loopCnt; ++l) { - begins.push_back(propCtr.readUint16(ptCsr)); - ptCsr += 2; - ends.push_back(propCtr.readUint16(ptCsr)); - ptCsr += 2; - times.push_back(propCtr.readUint8(ptCsr++)); - } - instManLocked->setPitchADPCMLoops(ptNum, begins, ends, times); + for (uint16_t l = 0; l < loopCnt; ++l) { + int begin = propCtr.readUint16(ptCsr); + ptCsr += 2; + int end = propCtr.readUint16(ptCsr); + ptCsr += 2; + int times = propCtr.readUint8(ptCsr++); + instManLocked->addPitchADPCMLoop(ptNum, InstrumentSequenceLoop(begin, end, times)); } switch (propCtr.readUint8(ptCsr++)) { case 0x00: // No release - instManLocked->setPitchADPCMRelease(ptNum, ReleaseType::NoRelease, -1); + instManLocked->setPitchADPCMRelease(ptNum, InstrumentSequenceRelease(InstrumentSequenceRelease::NoRelease)); break; case 0x01: // Fixed { @@ -1944,8 +1903,8 @@ ptCsr += 2; // Release point check (prevents a bug) // https://github.com/rerrahkr/BambooTracker/issues/11 - if (pos < seqLen) instManLocked->setPitchADPCMRelease(ptNum, ReleaseType::FixedRelease, pos); - else instManLocked->setPitchADPCMRelease(ptNum, ReleaseType::NoRelease, -1); + if (pos < seqLen) instManLocked->setPitchADPCMRelease(ptNum, InstrumentSequenceRelease(InstrumentSequenceRelease::FixedRelease, pos)); + else instManLocked->setPitchADPCMRelease(ptNum, InstrumentSequenceRelease(InstrumentSequenceRelease::NoRelease)); break; } default: @@ -1954,10 +1913,10 @@ switch (propCtr.readUint8(ptCsr++)) { case 0x00: // Absolute - instManLocked->setPitchADPCMType(ptNum, SequenceType::ABSOLUTE_SEQUENCE); + instManLocked->setPitchADPCMType(ptNum, SequenceType::AbsoluteSequence); break; case 0x02: // Relative - instManLocked->setPitchADPCMType(ptNum, SequenceType::RELATIVE_SEQUENCE); + instManLocked->setPitchADPCMType(ptNum, SequenceType::RelativeSequence); break; default: throw FileCorruptionError(FileType::Bank, ptCsr); diff -Nru bambootracker-0.4.5+git20121209/BambooTracker/io/bti_io.cpp bambootracker-0.4.6/BambooTracker/io/bti_io.cpp --- bambootracker-0.4.5+git20121209/BambooTracker/io/bti_io.cpp 2020-12-08 14:05:28.000000000 +0000 +++ bambootracker-0.4.6/BambooTracker/io/bti_io.cpp 2021-02-11 14:37:17.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (C) 2020 Rerrah + * Copyright (C) 2020-2021 Rerrah * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation @@ -31,11 +31,73 @@ #include "enum_hash.hpp" #include "version.hpp" #include "file_io_error.hpp" -#include "calc_pitch.hpp" +#include "note.hpp" #include "io_utils.hpp" +#include "utils.hpp" namespace io { +namespace +{ +size_t loadInstrumentPropertyOperatorSequenceForInstrument( + FMEnvelopeParameter param, size_t instMemCsr, + std::shared_ptr& instManLocked, + const BinaryContainer& ctr, InstrumentFM* inst, int idx, uint32_t version) +{ + inst->setOperatorSequenceEnabled(param, true); + inst->setOperatorSequenceNumber(param, idx); + uint16_t ofs = ctr.readUint16(instMemCsr); + size_t csr = instMemCsr + 2; + + uint16_t seqLen = ctr.readUint16(csr); + csr += 2; + for (uint16_t l = 0; l < seqLen; ++l) { + uint16_t data = ctr.readUint16(csr); + csr += 2; + if (version < Version::toBCD(1, 2, 1)) csr += 2; + if (l == 0) + instManLocked->setOperatorSequenceFMSequenceData(param, idx, 0, data); + else + instManLocked->addOperatorSequenceFMSequenceData(param, idx, data); + } + + uint16_t loopCnt = ctr.readUint16(csr); + csr += 2; + for (uint16_t l = 0; l < loopCnt; ++l) { + int begin = ctr.readUint16(csr); + csr += 2; + int end = ctr.readUint16(csr); + csr += 2; + int times = ctr.readUint8(csr++); + instManLocked->addOperatorSequenceFMLoop(param, idx, InstrumentSequenceLoop(begin, end, times)); + } + + switch (ctr.readUint8(csr++)) { + case 0x00: // No release + instManLocked->setOperatorSequenceFMRelease(param, idx, InstrumentSequenceRelease(InstrumentSequenceRelease::NoRelease)); + break; + case 0x01: // Fixed + { + uint16_t pos = ctr.readUint16(csr); + csr += 2; + // Release point check (prevents a bug) + // https://github.com/rerrahkr/BambooTracker/issues/11 + if (pos < seqLen) instManLocked->setOperatorSequenceFMRelease(param, idx, InstrumentSequenceRelease(InstrumentSequenceRelease::FixedRelease, pos)); + else instManLocked->setOperatorSequenceFMRelease(param, idx, InstrumentSequenceRelease(InstrumentSequenceRelease::NoRelease)); + break; + } + default: + throw FileCorruptionError(FileType::Inst, csr); + } + + if (version >= Version::toBCD(1, 0, 1)) { + ++csr; // Skip sequence type + } + + return ofs; +} +} + BtiIO::BtiIO() : AbstractInstrumentIO("bti", "BambooTracker instrument", true, true) {} AbstractInstrument* BtiIO::load(const BinaryContainer& ctr, const std::string& fileName, @@ -865,28 +927,25 @@ csr += 2; if (fileVersion < Version::toBCD(1, 2, 0)) csr += 2; if (l == 0) - instManLocked->setArpeggioFMSequenceCommand(idx, 0, data, 0); + instManLocked->setArpeggioFMSequenceData(idx, 0, data); else - instManLocked->addArpeggioFMSequenceCommand(idx, data, 0); + instManLocked->addArpeggioFMSequenceData(idx, data); } uint16_t loopCnt = ctr.readUint16(csr); csr += 2; - if (loopCnt > 0) { - std::vector begins, ends, times; - for (uint16_t l = 0; l < loopCnt; ++l) { - begins.push_back(ctr.readUint16(csr)); - csr += 2; - ends.push_back(ctr.readUint16(csr)); - csr += 2; - times.push_back(ctr.readUint8(csr++)); - } - instManLocked->setArpeggioFMLoops(idx, begins, ends, times); + for (uint16_t l = 0; l < loopCnt; ++l) { + int begin = ctr.readUint16(csr); + csr += 2; + int end = ctr.readUint16(csr); + csr += 2; + int times = ctr.readUint8(csr++); + instManLocked->addArpeggioFMLoop(idx, InstrumentSequenceLoop(begin, end, times)); } switch (ctr.readUint8(csr++)) { case 0x00: // No release - instManLocked->setArpeggioFMRelease(idx, ReleaseType::NoRelease, -1); + instManLocked->setArpeggioFMRelease(idx, InstrumentSequenceRelease(InstrumentSequenceRelease::NoRelease)); break; case 0x01: // Fixed { @@ -894,8 +953,8 @@ csr += 2; // Release point check (prevents a bug) // https://github.com/rerrahkr/BambooTracker/issues/11 - if (pos < seqLen) instManLocked->setArpeggioFMRelease(idx, ReleaseType::FixedRelease, pos); - else instManLocked->setArpeggioFMRelease(idx, ReleaseType::NoRelease, -1); + if (pos < seqLen) instManLocked->setArpeggioFMRelease(idx, InstrumentSequenceRelease(InstrumentSequenceRelease::FixedRelease, pos)); + else instManLocked->setArpeggioFMRelease(idx, InstrumentSequenceRelease(InstrumentSequenceRelease::NoRelease)); break; } default: @@ -905,19 +964,19 @@ if (fileVersion >= Version::toBCD(1, 0, 1)) { switch (ctr.readUint8(csr++)) { case 0x00: // Absolute - instManLocked->setArpeggioFMType(idx, SequenceType::ABSOLUTE_SEQUENCE); + instManLocked->setArpeggioFMType(idx, SequenceType::AbsoluteSequence); break; case 0x01: // Fixed - instManLocked->setArpeggioFMType(idx, SequenceType::FIXED_SEQUENCE); + instManLocked->setArpeggioFMType(idx, SequenceType::FixedSequence); break; case 0x02: // Relative - instManLocked->setArpeggioFMType(idx, SequenceType::RELATIVE_SEQUENCE); + instManLocked->setArpeggioFMType(idx, SequenceType::RelativeSequence); break; default: if (fileVersion < Version::toBCD(1, 2, 3)) { // Recover deep clone bug // https://github.com/rerrahkr/BambooTracker/issues/170 - instManLocked->setArpeggioFMType(idx, SequenceType::ABSOLUTE_SEQUENCE); + instManLocked->setArpeggioFMType(idx, SequenceType::AbsoluteSequence); break; } else { @@ -958,28 +1017,25 @@ csr += 2; if (fileVersion < Version::toBCD(1, 2, 0)) csr += 2; if (l == 0) - instManLocked->setPitchFMSequenceCommand(idx, 0, data, 0); + instManLocked->setPitchFMSequenceData(idx, 0, data); else - instManLocked->addPitchFMSequenceCommand(idx, data, 0); + instManLocked->addPitchFMSequenceData(idx, data); } uint16_t loopCnt = ctr.readUint16(csr); csr += 2; - if (loopCnt > 0) { - std::vector begins, ends, times; - for (uint16_t l = 0; l < loopCnt; ++l) { - begins.push_back(ctr.readUint16(csr)); - csr += 2; - ends.push_back(ctr.readUint16(csr)); - csr += 2; - times.push_back(ctr.readUint8(csr++)); - } - instManLocked->setPitchFMLoops(idx, begins, ends, times); + for (uint16_t l = 0; l < loopCnt; ++l) { + int begin = ctr.readUint16(csr); + csr += 2; + int end = ctr.readUint16(csr); + csr += 2; + int times = ctr.readUint8(csr++); + instManLocked->addPitchFMLoop(idx, InstrumentSequenceLoop(begin, end, times)); } switch (ctr.readUint8(csr++)) { case 0x00: // No release - instManLocked->setPitchFMRelease(idx, ReleaseType::NoRelease, -1); + instManLocked->setPitchFMRelease(idx, InstrumentSequenceRelease(InstrumentSequenceRelease::NoRelease)); break; case 0x01: // Fixed { @@ -987,8 +1043,8 @@ csr += 2; // Release point check (prevents a bug) // https://github.com/rerrahkr/BambooTracker/issues/11 - if (pos < seqLen) instManLocked->setPitchFMRelease(idx, ReleaseType::FixedRelease, pos); - else instManLocked->setPitchFMRelease(idx, ReleaseType::NoRelease, -1); + if (pos < seqLen) instManLocked->setPitchFMRelease(idx, InstrumentSequenceRelease(InstrumentSequenceRelease::FixedRelease, pos)); + else instManLocked->setPitchFMRelease(idx, InstrumentSequenceRelease(InstrumentSequenceRelease::NoRelease)); break; } default: @@ -998,16 +1054,16 @@ if (fileVersion >= Version::toBCD(1, 0, 1)) { switch (ctr.readUint8(csr++)) { case 0x00: // Absolute - instManLocked->setPitchFMType(idx, SequenceType::ABSOLUTE_SEQUENCE); + instManLocked->setPitchFMType(idx, SequenceType::AbsoluteSequence); break; case 0x02: // Relative - instManLocked->setPitchFMType(idx, SequenceType::RELATIVE_SEQUENCE); + instManLocked->setPitchFMType(idx, SequenceType::RelativeSequence); break; default: if (fileVersion < Version::toBCD(1, 2, 3)) { // Recover deep clone bug // https://github.com/rerrahkr/BambooTracker/issues/170 - instManLocked->setPitchFMType(idx, SequenceType::ABSOLUTE_SEQUENCE); + instManLocked->setPitchFMType(idx, SequenceType::AbsoluteSequence); break; } else { @@ -1034,8 +1090,8 @@ uint16_t data = ctr.readUint16(csr); csr += 2; if (fileVersion < Version::toBCD(1, 2, 0)) { - if (data == 3) data = static_cast(SSGWaveformType::SQM_TRIANGLE); - else if (data == 4) data = static_cast(SSGWaveformType::SQM_SAW); + if (data == 3) data = SSGWaveformType::SQM_TRIANGLE; + else if (data == 4) data = SSGWaveformType::SQM_SAW; } int32_t subdata; if (fileVersion >= Version::toBCD(1, 2, 0)) { @@ -1046,31 +1102,39 @@ subdata = ctr.readUint16(csr); csr += 2; if (subdata != -1) - subdata = calc_pitch::calculateSSGSquareTP(subdata); + subdata = note_utils::calculateSSGSquareTP(subdata, 0); + } + SSGWaveformUnit unit; + switch (data) { + case SSGWaveformType::SQM_TRIANGLE: + case SSGWaveformType::SQM_SAW: + case SSGWaveformType::SQM_INVSAW: + unit = SSGWaveformUnit::makeUnitWithDecode(data, subdata); + break; + default: + unit = SSGWaveformUnit::makeOnlyDataUnit(data); + break; } if (l == 0) - instManLocked->setWaveformSSGSequenceCommand(idx, 0, data, subdata); + instManLocked->setWaveformSSGSequenceData(idx, 0, unit); else - instManLocked->addWaveformSSGSequenceCommand(idx, data, subdata); + instManLocked->addWaveformSSGSequenceData(idx, unit); } uint16_t loopCnt = ctr.readUint16(csr); csr += 2; - if (loopCnt > 0) { - std::vector begins, ends, times; - for (uint16_t l = 0; l < loopCnt; ++l) { - begins.push_back(ctr.readUint16(csr)); - csr += 2; - ends.push_back(ctr.readUint16(csr)); - csr += 2; - times.push_back(ctr.readUint8(csr++)); - } - instManLocked->setWaveformSSGLoops(idx, begins, ends, times); + for (uint16_t l = 0; l < loopCnt; ++l) { + int begin = ctr.readUint16(csr); + csr += 2; + int end = ctr.readUint16(csr); + csr += 2; + int times = ctr.readUint8(csr++); + instManLocked->addWaveformSSGLoop(idx, InstrumentSequenceLoop(begin, end, times)); } switch (ctr.readUint8(csr++)) { case 0x00: // No release - instManLocked->setWaveformSSGRelease(idx, ReleaseType::NoRelease, -1); + instManLocked->setWaveformSSGRelease(idx, InstrumentSequenceRelease(InstrumentSequenceRelease::NoRelease)); break; case 0x01: // Fixed { @@ -1078,8 +1142,8 @@ csr += 2; // Release point check (prevents a bug) // https://github.com/rerrahkr/BambooTracker/issues/11 - if (pos < seqLen) instManLocked->setWaveformSSGRelease(idx, ReleaseType::FixedRelease, pos); - else instManLocked->setWaveformSSGRelease(idx, ReleaseType::NoRelease, -1); + if (pos < seqLen) instManLocked->setWaveformSSGRelease(idx, InstrumentSequenceRelease(InstrumentSequenceRelease::FixedRelease, pos)); + else instManLocked->setWaveformSSGRelease(idx, InstrumentSequenceRelease(InstrumentSequenceRelease::NoRelease)); break; } default: @@ -1115,28 +1179,25 @@ } if (fileVersion < Version::toBCD(1, 2, 0)) csr += 2; if (l == 0) - instManLocked->setToneNoiseSSGSequenceCommand(idx, 0, data, 0); + instManLocked->setToneNoiseSSGSequenceData(idx, 0, data); else - instManLocked->addToneNoiseSSGSequenceCommand(idx, data, 0); + instManLocked->addToneNoiseSSGSequenceData(idx, data); } uint16_t loopCnt = ctr.readUint16(csr); csr += 2; - if (loopCnt > 0) { - std::vector begins, ends, times; - for (uint16_t l = 0; l < loopCnt; ++l) { - begins.push_back(ctr.readUint16(csr)); - csr += 2; - ends.push_back(ctr.readUint16(csr)); - csr += 2; - times.push_back(ctr.readUint8(csr++)); - } - instManLocked->setToneNoiseSSGLoops(idx, begins, ends, times); + for (uint16_t l = 0; l < loopCnt; ++l) { + int begin = ctr.readUint16(csr); + csr += 2; + int end = ctr.readUint16(csr); + csr += 2; + int times = ctr.readUint8(csr++); + instManLocked->addToneNoiseSSGLoop(idx, InstrumentSequenceLoop(begin, end, times)); } switch (ctr.readUint8(csr++)) { case 0x00: // No release - instManLocked->setToneNoiseSSGRelease(idx, ReleaseType::NoRelease, -1); + instManLocked->setToneNoiseSSGRelease(idx, InstrumentSequenceRelease(InstrumentSequenceRelease::NoRelease)); break; case 0x01: // Fixed { @@ -1144,8 +1205,8 @@ csr += 2; // Release point check (prevents a bug) // https://github.com/rerrahkr/BambooTracker/issues/11 - if (pos < seqLen) instManLocked->setToneNoiseSSGRelease(idx, ReleaseType::FixedRelease, pos); - else instManLocked->setToneNoiseSSGRelease(idx, ReleaseType::NoRelease, -1); + if (pos < seqLen) instManLocked->setToneNoiseSSGRelease(idx, InstrumentSequenceRelease(InstrumentSequenceRelease::FixedRelease, pos)); + else instManLocked->setToneNoiseSSGRelease(idx, InstrumentSequenceRelease(InstrumentSequenceRelease::NoRelease)); break; } default: @@ -1182,29 +1243,28 @@ subdata = ctr.readUint16(csr); csr += 2; } + SSGEnvelopeUnit unit = (data < 16) ? SSGEnvelopeUnit::makeOnlyDataUnit(data) + : SSGEnvelopeUnit::makeUnitWithDecode(data, subdata); if (l == 0) - instManLocked->setEnvelopeSSGSequenceCommand(idx, 0, data, subdata); + instManLocked->setEnvelopeSSGSequenceData(idx, 0, unit); else - instManLocked->addEnvelopeSSGSequenceCommand(idx, data, subdata); + instManLocked->addEnvelopeSSGSequenceData(idx, unit); } uint16_t loopCnt = ctr.readUint16(csr); csr += 2; - if (loopCnt > 0) { - std::vector begins, ends, times; - for (uint16_t l = 0; l < loopCnt; ++l) { - begins.push_back(ctr.readUint16(csr)); - csr += 2; - ends.push_back(ctr.readUint16(csr)); - csr += 2; - times.push_back(ctr.readUint8(csr++)); - } - instManLocked->setEnvelopeSSGLoops(idx, begins, ends, times); + for (uint16_t l = 0; l < loopCnt; ++l) { + int begin = ctr.readUint16(csr); + csr += 2; + int end = ctr.readUint16(csr); + csr += 2; + int times = ctr.readUint8(csr++); + instManLocked->addEnvelopeSSGLoop(idx, InstrumentSequenceLoop(begin, end, times)); } switch (ctr.readUint8(csr++)) { case 0x00: // No release - instManLocked->setEnvelopeSSGRelease(idx, ReleaseType::NoRelease, -1); + instManLocked->setEnvelopeSSGRelease(idx, InstrumentSequenceRelease(InstrumentSequenceRelease::NoRelease)); break; // Release point check (prevents a bug) // https://github.com/rerrahkr/BambooTracker/issues/11 @@ -1212,24 +1272,24 @@ { uint16_t pos = ctr.readUint16(csr); csr += 2; - if (pos < seqLen) instManLocked->setEnvelopeSSGRelease(idx, ReleaseType::FixedRelease, pos); - else instManLocked->setEnvelopeSSGRelease(idx, ReleaseType::NoRelease, -1); + if (pos < seqLen) instManLocked->setEnvelopeSSGRelease(idx, InstrumentSequenceRelease(InstrumentSequenceRelease::FixedRelease, pos)); + else instManLocked->setEnvelopeSSGRelease(idx, InstrumentSequenceRelease(InstrumentSequenceRelease::NoRelease)); break; } case 0x02: // Absolute { uint16_t pos = ctr.readUint16(csr); csr += 2; - if (pos < seqLen) instManLocked->setEnvelopeSSGRelease(idx, ReleaseType::AbsoluteRelease, pos); - else instManLocked->setEnvelopeSSGRelease(idx, ReleaseType::NoRelease, -1); + if (pos < seqLen) instManLocked->setEnvelopeSSGRelease(idx, InstrumentSequenceRelease(InstrumentSequenceRelease::AbsoluteRelease, pos)); + else instManLocked->setEnvelopeSSGRelease(idx, InstrumentSequenceRelease(InstrumentSequenceRelease::NoRelease)); break; } case 0x03: // Relative { uint16_t pos = ctr.readUint16(csr); csr += 2; - if (pos < seqLen) instManLocked->setEnvelopeSSGRelease(idx, ReleaseType::RelativeRelease, pos); - else instManLocked->setEnvelopeSSGRelease(idx, ReleaseType::NoRelease, -1); + if (pos < seqLen) instManLocked->setEnvelopeSSGRelease(idx, InstrumentSequenceRelease(InstrumentSequenceRelease::RelativeRelease, pos)); + else instManLocked->setEnvelopeSSGRelease(idx, InstrumentSequenceRelease(InstrumentSequenceRelease::NoRelease)); break; } default: @@ -1259,28 +1319,25 @@ csr += 2; if (fileVersion < Version::toBCD(1, 2, 0)) csr += 2; if (l == 0) - instManLocked->setArpeggioSSGSequenceCommand(idx, 0, data, 0); + instManLocked->setArpeggioSSGSequenceData(idx, 0, data); else - instManLocked->addArpeggioSSGSequenceCommand(idx, data, 0); + instManLocked->addArpeggioSSGSequenceData(idx, data); } uint16_t loopCnt = ctr.readUint16(csr); csr += 2; - if (loopCnt > 0) { - std::vector begins, ends, times; - for (uint16_t l = 0; l < loopCnt; ++l) { - begins.push_back(ctr.readUint16(csr)); - csr += 2; - ends.push_back(ctr.readUint16(csr)); - csr += 2; - times.push_back(ctr.readUint8(csr++)); - } - instManLocked->setArpeggioSSGLoops(idx, begins, ends, times); + for (uint16_t l = 0; l < loopCnt; ++l) { + int begin = ctr.readUint16(csr); + csr += 2; + int end = ctr.readUint16(csr); + csr += 2; + int times = ctr.readUint8(csr++); + instManLocked->addArpeggioSSGLoop(idx, InstrumentSequenceLoop(begin, end, times)); } switch (ctr.readUint8(csr++)) { case 0x00: // No release - instManLocked->setArpeggioSSGRelease(idx, ReleaseType::NoRelease, -1); + instManLocked->setArpeggioSSGRelease(idx, InstrumentSequenceRelease(InstrumentSequenceRelease::NoRelease)); break; case 0x01: // Fixed { @@ -1288,8 +1345,8 @@ csr += 2; // Release point check (prevents a bug) // https://github.com/rerrahkr/BambooTracker/issues/11 - if (pos < seqLen) instManLocked->setArpeggioSSGRelease(idx, ReleaseType::FixedRelease, pos); - else instManLocked->setArpeggioSSGRelease(idx, ReleaseType::NoRelease, -1); + if (pos < seqLen) instManLocked->setArpeggioSSGRelease(idx, InstrumentSequenceRelease(InstrumentSequenceRelease::FixedRelease, pos)); + else instManLocked->setArpeggioSSGRelease(idx, InstrumentSequenceRelease(InstrumentSequenceRelease::NoRelease)); break; } default: @@ -1299,19 +1356,19 @@ if (fileVersion >= Version::toBCD(1, 0, 1)) { switch (ctr.readUint8(csr++)) { case 0x00: // Absolute - instManLocked->setArpeggioSSGType(idx, SequenceType::ABSOLUTE_SEQUENCE); + instManLocked->setArpeggioSSGType(idx, SequenceType::AbsoluteSequence); break; case 0x01: // Fixed - instManLocked->setArpeggioSSGType(idx, SequenceType::FIXED_SEQUENCE); + instManLocked->setArpeggioSSGType(idx, SequenceType::FixedSequence); break; case 0x02: // Relative - instManLocked->setArpeggioSSGType(idx, SequenceType::RELATIVE_SEQUENCE); + instManLocked->setArpeggioSSGType(idx, SequenceType::RelativeSequence); break; default: if (fileVersion < Version::toBCD(1, 2, 3)) { // Recover deep clone bug // https://github.com/rerrahkr/BambooTracker/issues/170 - instManLocked->setArpeggioSSGType(idx, SequenceType::ABSOLUTE_SEQUENCE); + instManLocked->setArpeggioSSGType(idx, SequenceType::AbsoluteSequence); break; } else { @@ -1339,28 +1396,25 @@ csr += 2; if (fileVersion < Version::toBCD(1, 2, 0)) csr += 2; if (l == 0) - instManLocked->setPitchSSGSequenceCommand(idx, 0, data, 0); + instManLocked->setPitchSSGSequenceData(idx, 0, data); else - instManLocked->addPitchSSGSequenceCommand(idx, data, 0); + instManLocked->addPitchSSGSequenceData(idx, data); } uint16_t loopCnt = ctr.readUint16(csr); csr += 2; - if (loopCnt > 0) { - std::vector begins, ends, times; - for (uint16_t l = 0; l < loopCnt; ++l) { - begins.push_back(ctr.readUint16(csr)); - csr += 2; - ends.push_back(ctr.readUint16(csr)); - csr += 2; - times.push_back(ctr.readUint8(csr++)); - } - instManLocked->setPitchSSGLoops(idx, begins, ends, times); + for (uint16_t l = 0; l < loopCnt; ++l) { + int begin = ctr.readUint16(csr); + csr += 2; + int end = ctr.readUint16(csr); + csr += 2; + int times = ctr.readUint8(csr++); + instManLocked->addPitchSSGLoop(idx, InstrumentSequenceLoop(begin, end, times)); } switch (ctr.readUint8(csr++)) { case 0x00: // No release - instManLocked->setPitchSSGRelease(idx, ReleaseType::NoRelease, -1); + instManLocked->setPitchSSGRelease(idx, InstrumentSequenceRelease(InstrumentSequenceRelease::NoRelease)); break; case 0x01: // Fixed { @@ -1368,8 +1422,8 @@ csr += 2; // Release point check (prevents a bug) // https://github.com/rerrahkr/BambooTracker/issues/11 - if (pos < seqLen) instManLocked->setPitchSSGRelease(idx, ReleaseType::FixedRelease, pos); - else instManLocked->setPitchSSGRelease(idx, ReleaseType::NoRelease, -1); + if (pos < seqLen) instManLocked->setPitchSSGRelease(idx, InstrumentSequenceRelease(InstrumentSequenceRelease::FixedRelease, pos)); + else instManLocked->setPitchSSGRelease(idx, InstrumentSequenceRelease(InstrumentSequenceRelease::NoRelease)); break; } default: @@ -1379,16 +1433,16 @@ if (fileVersion >= Version::toBCD(1, 0, 1)) { switch (ctr.readUint8(csr++)) { case 0x00: // Absolute - instManLocked->setPitchSSGType(idx, SequenceType::ABSOLUTE_SEQUENCE); + instManLocked->setPitchSSGType(idx, SequenceType::AbsoluteSequence); break; case 0x02: // Relative - instManLocked->setPitchSSGType(idx, SequenceType::RELATIVE_SEQUENCE); + instManLocked->setPitchSSGType(idx, SequenceType::RelativeSequence); break; default: if (fileVersion < Version::toBCD(1, 2, 3)) { // Recover deep clone bug // https://github.com/rerrahkr/BambooTracker/issues/170 - instManLocked->setPitchSSGType(idx, SequenceType::ABSOLUTE_SEQUENCE); + instManLocked->setPitchSSGType(idx, SequenceType::AbsoluteSequence); break; } else { @@ -1422,7 +1476,7 @@ uint32_t len = ctr.readUint32(csr); csr += 4; std::vector samples = ctr.getSubcontainer(csr, len).toVector(); - csr += len; + /* csr += len; */ instManLocked->storeSampleADPCMRawSample(idx, std::move(samples)); instPropCsr += ofs; @@ -1441,39 +1495,27 @@ csr += 2; for (uint16_t l = 0; l < seqLen; ++l) { uint16_t data = ctr.readUint16(csr); - csr += 2; - int32_t subdata; - if (fileVersion >= Version::toBCD(1, 2, 0)) { - subdata = ctr.readInt32(csr); - csr += 4; - } - else { - subdata = ctr.readUint16(csr); - csr += 2; - } + csr += 6; // Skip subdata if (l == 0) - instManLocked->setEnvelopeADPCMSequenceCommand(idx, 0, data, subdata); + instManLocked->setEnvelopeADPCMSequenceData(idx, 0, data); else - instManLocked->addEnvelopeADPCMSequenceCommand(idx, data, subdata); + instManLocked->addEnvelopeADPCMSequenceData(idx, data); } uint16_t loopCnt = ctr.readUint16(csr); csr += 2; - if (loopCnt > 0) { - std::vector begins, ends, times; - for (uint16_t l = 0; l < loopCnt; ++l) { - begins.push_back(ctr.readUint16(csr)); - csr += 2; - ends.push_back(ctr.readUint16(csr)); - csr += 2; - times.push_back(ctr.readUint8(csr++)); - } - instManLocked->setEnvelopeADPCMLoops(idx, begins, ends, times); + for (uint16_t l = 0; l < loopCnt; ++l) { + int begin = ctr.readUint16(csr); + csr += 2; + int end = ctr.readUint16(csr); + csr += 2; + int times = ctr.readUint8(csr++); + instManLocked->addEnvelopeADPCMLoop(idx, InstrumentSequenceLoop(begin, end, times)); } switch (ctr.readUint8(csr++)) { case 0x00: // No release - instManLocked->setEnvelopeADPCMRelease(idx, ReleaseType::NoRelease, -1); + instManLocked->setEnvelopeADPCMRelease(idx, InstrumentSequenceRelease(InstrumentSequenceRelease::NoRelease)); break; // Release point check (prevents a bug) // https://github.com/rerrahkr/BambooTracker/issues/11 @@ -1481,24 +1523,24 @@ { uint16_t pos = ctr.readUint16(csr); csr += 2; - if (pos < seqLen) instManLocked->setEnvelopeADPCMRelease(idx, ReleaseType::FixedRelease, pos); - else instManLocked->setEnvelopeADPCMRelease(idx, ReleaseType::NoRelease, -1); + if (pos < seqLen) instManLocked->setEnvelopeADPCMRelease(idx, InstrumentSequenceRelease(InstrumentSequenceRelease::FixedRelease, pos)); + else instManLocked->setEnvelopeADPCMRelease(idx, InstrumentSequenceRelease(InstrumentSequenceRelease::NoRelease)); break; } case 0x02: // Absolute { uint16_t pos = ctr.readUint16(csr); csr += 2; - if (pos < seqLen) instManLocked->setEnvelopeADPCMRelease(idx, ReleaseType::AbsoluteRelease, pos); - else instManLocked->setEnvelopeADPCMRelease(idx, ReleaseType::NoRelease, -1); + if (pos < seqLen) instManLocked->setEnvelopeADPCMRelease(idx, InstrumentSequenceRelease(InstrumentSequenceRelease::AbsoluteRelease, pos)); + else instManLocked->setEnvelopeADPCMRelease(idx, InstrumentSequenceRelease(InstrumentSequenceRelease::NoRelease)); break; } case 0x03: // Relative { uint16_t pos = ctr.readUint16(csr); csr += 2; - if (pos < seqLen) instManLocked->setEnvelopeADPCMRelease(idx, ReleaseType::RelativeRelease, pos); - else instManLocked->setEnvelopeADPCMRelease(idx, ReleaseType::NoRelease, -1); + if (pos < seqLen) instManLocked->setEnvelopeADPCMRelease(idx, InstrumentSequenceRelease(InstrumentSequenceRelease::RelativeRelease, pos)); + else instManLocked->setEnvelopeADPCMRelease(idx, InstrumentSequenceRelease(InstrumentSequenceRelease::NoRelease)); break; } default: @@ -1526,28 +1568,25 @@ csr += 2; if (fileVersion < Version::toBCD(1, 2, 0)) csr += 2; if (l == 0) - instManLocked->setArpeggioADPCMSequenceCommand(idx, 0, data, 0); + instManLocked->setArpeggioADPCMSequenceData(idx, 0, data); else - instManLocked->addArpeggioADPCMSequenceCommand(idx, data, 0); + instManLocked->addArpeggioADPCMSequenceData(idx, data); } uint16_t loopCnt = ctr.readUint16(csr); csr += 2; - if (loopCnt > 0) { - std::vector begins, ends, times; - for (uint16_t l = 0; l < loopCnt; ++l) { - begins.push_back(ctr.readUint16(csr)); - csr += 2; - ends.push_back(ctr.readUint16(csr)); - csr += 2; - times.push_back(ctr.readUint8(csr++)); - } - instManLocked->setArpeggioADPCMLoops(idx, begins, ends, times); + for (uint16_t l = 0; l < loopCnt; ++l) { + int begin = ctr.readUint16(csr); + csr += 2; + int end = ctr.readUint16(csr); + csr += 2; + int times = ctr.readUint8(csr++); + instManLocked->addArpeggioADPCMLoop(idx, InstrumentSequenceLoop(begin, end, times)); } switch (ctr.readUint8(csr++)) { case 0x00: // No release - instManLocked->setArpeggioADPCMRelease(idx, ReleaseType::NoRelease, -1); + instManLocked->setArpeggioADPCMRelease(idx, InstrumentSequenceRelease(InstrumentSequenceRelease::NoRelease)); break; case 0x01: // Fixed { @@ -1555,8 +1594,8 @@ csr += 2; // Release point check (prevents a bug) // https://github.com/rerrahkr/BambooTracker/issues/11 - if (pos < seqLen) instManLocked->setArpeggioADPCMRelease(idx, ReleaseType::FixedRelease, pos); - else instManLocked->setArpeggioADPCMRelease(idx, ReleaseType::NoRelease, -1); + if (pos < seqLen) instManLocked->setArpeggioADPCMRelease(idx, InstrumentSequenceRelease(InstrumentSequenceRelease::FixedRelease, pos)); + else instManLocked->setArpeggioADPCMRelease(idx, InstrumentSequenceRelease(InstrumentSequenceRelease::NoRelease)); break; } default: @@ -1565,13 +1604,13 @@ switch (ctr.readUint8(csr++)) { case 0x00: // Absolute - instManLocked->setArpeggioADPCMType(idx, SequenceType::ABSOLUTE_SEQUENCE); + instManLocked->setArpeggioADPCMType(idx, SequenceType::AbsoluteSequence); break; case 0x01: // Fixed - instManLocked->setArpeggioADPCMType(idx, SequenceType::FIXED_SEQUENCE); + instManLocked->setArpeggioADPCMType(idx, SequenceType::FixedSequence); break; case 0x02: // Relative - instManLocked->setArpeggioADPCMType(idx, SequenceType::RELATIVE_SEQUENCE); + instManLocked->setArpeggioADPCMType(idx, SequenceType::RelativeSequence); break; default: throw FileCorruptionError(FileType::Inst, csr); @@ -1596,28 +1635,25 @@ csr += 2; if (fileVersion < Version::toBCD(1, 2, 0)) csr += 2; if (l == 0) - instManLocked->setPitchADPCMSequenceCommand(idx, 0, data, 0); + instManLocked->setPitchADPCMSequenceData(idx, 0, data); else - instManLocked->addPitchADPCMSequenceCommand(idx, data, 0); + instManLocked->addPitchADPCMSequenceData(idx, data); } uint16_t loopCnt = ctr.readUint16(csr); csr += 2; - if (loopCnt > 0) { - std::vector begins, ends, times; - for (uint16_t l = 0; l < loopCnt; ++l) { - begins.push_back(ctr.readUint16(csr)); - csr += 2; - ends.push_back(ctr.readUint16(csr)); - csr += 2; - times.push_back(ctr.readUint8(csr++)); - } - instManLocked->setPitchADPCMLoops(idx, begins, ends, times); + for (uint16_t l = 0; l < loopCnt; ++l) { + int begin = ctr.readUint16(csr); + csr += 2; + int end = ctr.readUint16(csr); + csr += 2; + int times = ctr.readUint8(csr++); + instManLocked->addPitchADPCMLoop(idx, InstrumentSequenceLoop(begin, end, times)); } switch (ctr.readUint8(csr++)) { case 0x00: // No release - instManLocked->setPitchADPCMRelease(idx, ReleaseType::NoRelease, -1); + instManLocked->setPitchADPCMRelease(idx, InstrumentSequenceRelease(InstrumentSequenceRelease::NoRelease)); break; case 0x01: // Fixed { @@ -1625,8 +1661,8 @@ csr += 2; // Release point check (prevents a bug) // https://github.com/rerrahkr/BambooTracker/issues/11 - if (pos < seqLen) instManLocked->setPitchADPCMRelease(idx, ReleaseType::FixedRelease, pos); - else instManLocked->setPitchADPCMRelease(idx, ReleaseType::NoRelease, -1); + if (pos < seqLen) instManLocked->setPitchADPCMRelease(idx, InstrumentSequenceRelease(InstrumentSequenceRelease::FixedRelease, pos)); + else instManLocked->setPitchADPCMRelease(idx, InstrumentSequenceRelease(InstrumentSequenceRelease::NoRelease)); break; } default: @@ -1635,10 +1671,10 @@ switch (ctr.readUint8(csr++)) { case 0x00: // Absolute - instManLocked->setPitchADPCMType(idx, SequenceType::ABSOLUTE_SEQUENCE); + instManLocked->setPitchADPCMType(idx, SequenceType::AbsoluteSequence); break; case 0x02: // Relative - instManLocked->setPitchADPCMType(idx, SequenceType::RELATIVE_SEQUENCE); + instManLocked->setPitchADPCMType(idx, SequenceType::RelativeSequence); break; default: throw FileCorruptionError(FileType::Inst, csr); @@ -1658,67 +1694,6 @@ } } -size_t BtiIO::loadInstrumentPropertyOperatorSequenceForInstrument( - FMEnvelopeParameter param, size_t instMemCsr, - std::shared_ptr& instManLocked, - const BinaryContainer& ctr, InstrumentFM* inst, int idx, uint32_t version) -{ - inst->setOperatorSequenceEnabled(param, true); - inst->setOperatorSequenceNumber(param, idx); - uint16_t ofs = ctr.readUint16(instMemCsr); - size_t csr = instMemCsr + 2; - - uint16_t seqLen = ctr.readUint16(csr); - csr += 2; - for (uint16_t l = 0; l < seqLen; ++l) { - uint16_t data = ctr.readUint16(csr); - csr += 2; - if (version < Version::toBCD(1, 2, 1)) csr += 2; - if (l == 0) - instManLocked->setOperatorSequenceFMSequenceCommand(param, idx, 0, data, 0); - else - instManLocked->addOperatorSequenceFMSequenceCommand(param, idx, data, 0); - } - - uint16_t loopCnt = ctr.readUint16(csr); - csr += 2; - if (loopCnt > 0) { - std::vector begins, ends, times; - for (uint16_t l = 0; l < loopCnt; ++l) { - begins.push_back(ctr.readUint16(csr)); - csr += 2; - ends.push_back(ctr.readUint16(csr)); - csr += 2; - times.push_back(ctr.readUint8(csr++)); - } - instManLocked->setOperatorSequenceFMLoops(param, idx, begins, ends, times); - } - - switch (ctr.readUint8(csr++)) { - case 0x00: // No release - instManLocked->setOperatorSequenceFMRelease(param, idx, ReleaseType::NoRelease, -1); - break; - case 0x01: // Fixed - { - uint16_t pos = ctr.readUint16(csr); - csr += 2; - // Release point check (prevents a bug) - // https://github.com/rerrahkr/BambooTracker/issues/11 - if (pos < seqLen) instManLocked->setOperatorSequenceFMRelease(param, idx, ReleaseType::FixedRelease, pos); - else instManLocked->setOperatorSequenceFMRelease(param, idx, ReleaseType::NoRelease, -1); - break; - } - default: - throw FileCorruptionError(FileType::Inst, csr); - } - - if (version >= Version::toBCD(1, 0, 1)) { - ++csr; // Skip sequence type - } - - return ofs; -} - void BtiIO::save(BinaryContainer& ctr, const std::weak_ptr instMan, int instNum) const { @@ -1758,7 +1733,7 @@ for (auto& t : FM_OP_TYPES) { if (instFM->getArpeggioEnabled(t)) { int n = instFM->getArpeggioNumber(t); - if (std::find(fmArpNums.begin(), fmArpNums.end(), n) == fmArpNums.end()) + if (utils::find(fmArpNums, n) == fmArpNums.end()) fmArpNums.push_back(n); } } @@ -1793,7 +1768,7 @@ for (auto& t : FM_OP_TYPES) { if (instFM->getPitchEnabled(t)) { int n = instFM->getPitchNumber(t); - if (std::find(fmPtNums.begin(), fmPtNums.end(), n) == fmPtNums.end()) + if (utils::find(fmPtNums, n) == fmPtNums.end()) fmPtNums.push_back(n); } } @@ -1930,32 +1905,32 @@ ctr.appendUint16(0); // Dummy offset auto seq = instManLocked->getOperatorSequenceFMSequence(FM_OPSEQ_PARAMS[i], seqNum); ctr.appendUint16(static_cast(seq.size())); - for (auto& com : seq) { - ctr.appendUint16(static_cast(com.type)); + for (auto& unit : seq) { + ctr.appendUint16(static_cast(unit.data)); } - auto loop = instManLocked->getOperatorSequenceFMLoops(FM_OPSEQ_PARAMS[i], seqNum); - ctr.appendUint16(static_cast(loop.size())); - for (auto& l : loop) { - ctr.appendUint16(static_cast(l.begin)); - ctr.appendUint16(static_cast(l.end)); - ctr.appendUint8(static_cast(l.times)); + auto loops = instManLocked->getOperatorSequenceFMLoopRoot(FM_OPSEQ_PARAMS[i], seqNum).getAllLoops(); + ctr.appendUint16(static_cast(loops.size())); + for (auto& loop : loops) { + ctr.appendUint16(static_cast(loop.getBeginPos())); + ctr.appendUint16(static_cast(loop.getEndPos())); + ctr.appendUint8(static_cast(loop.getTimes())); } auto release = instManLocked->getOperatorSequenceFMRelease(FM_OPSEQ_PARAMS[i], seqNum); - switch (release.type) { - case ReleaseType::NoRelease: + switch (release.getType()) { + case InstrumentSequenceRelease::NoRelease: ctr.appendUint8(0x00); break; - case ReleaseType::FixedRelease: + case InstrumentSequenceRelease::FixedRelease: ctr.appendUint8(0x01); - ctr.appendUint16(static_cast(release.begin)); + ctr.appendUint16(static_cast(release.getBeginPos())); break; - case ReleaseType::AbsoluteRelease: + case InstrumentSequenceRelease::AbsoluteRelease: ctr.appendUint8(0x02); - ctr.appendUint16(static_cast(release.begin)); + ctr.appendUint16(static_cast(release.getBeginPos())); break; - case ReleaseType::RelativeRelease: + case InstrumentSequenceRelease::RelativeRelease: ctr.appendUint8(0x03); - ctr.appendUint16(static_cast(release.begin)); + ctr.appendUint16(static_cast(release.getBeginPos())); break; } ctr.appendUint8(0); // Skip sequence type @@ -1970,38 +1945,38 @@ ctr.appendUint16(0); // Dummy offset auto seq = instManLocked->getArpeggioFMSequence(arpNum); ctr.appendUint16(static_cast(seq.size())); - for (auto& com : seq) { - ctr.appendUint16(static_cast(com.type)); + for (auto& unit : seq) { + ctr.appendUint16(static_cast(unit.data)); } - auto loop = instManLocked->getArpeggioFMLoops(arpNum); - ctr.appendUint16(static_cast(loop.size())); - for (auto& l : loop) { - ctr.appendUint16(static_cast(l.begin)); - ctr.appendUint16(static_cast(l.end)); - ctr.appendUint8(static_cast(l.times)); + auto loops = instManLocked->getArpeggioFMLoopRoot(arpNum).getAllLoops(); + ctr.appendUint16(static_cast(loops.size())); + for (auto& loop : loops) { + ctr.appendUint16(static_cast(loop.getBeginPos())); + ctr.appendUint16(static_cast(loop.getEndPos())); + ctr.appendUint8(static_cast(loop.getTimes())); } auto release = instManLocked->getArpeggioFMRelease(arpNum); - switch (release.type) { - case ReleaseType::NoRelease: + switch (release.getType()) { + case InstrumentSequenceRelease::NoRelease: ctr.appendUint8(0x00); break; - case ReleaseType::FixedRelease: + case InstrumentSequenceRelease::FixedRelease: ctr.appendUint8(0x01); - ctr.appendUint16(static_cast(release.begin)); + ctr.appendUint16(static_cast(release.getBeginPos())); break; - case ReleaseType::AbsoluteRelease: + case InstrumentSequenceRelease::AbsoluteRelease: ctr.appendUint8(0x02); - ctr.appendUint16(static_cast(release.begin)); + ctr.appendUint16(static_cast(release.getBeginPos())); break; - case ReleaseType::RelativeRelease: + case InstrumentSequenceRelease::RelativeRelease: ctr.appendUint8(0x03); - ctr.appendUint16(static_cast(release.begin)); + ctr.appendUint16(static_cast(release.getBeginPos())); break; } switch (instManLocked->getArpeggioFMType(arpNum)) { - case SequenceType::ABSOLUTE_SEQUENCE: ctr.appendUint8(0x00); break; - case SequenceType::FIXED_SEQUENCE: ctr.appendUint8(0x01); break; - case SequenceType::RELATIVE_SEQUENCE: ctr.appendUint8(0x02); break; + case SequenceType::AbsoluteSequence: ctr.appendUint8(0x00); break; + case SequenceType::FixedSequence: ctr.appendUint8(0x01); break; + case SequenceType::RelativeSequence: ctr.appendUint8(0x02); break; default: break; } ctr.writeUint16(ofs, static_cast(ctr.size() - ofs)); @@ -2014,37 +1989,37 @@ ctr.appendUint16(0); // Dummy offset auto seq = instManLocked->getPitchFMSequence(ptNum); ctr.appendUint16(static_cast(seq.size())); - for (auto& com : seq) { - ctr.appendUint16(static_cast(com.type)); + for (auto& unit : seq) { + ctr.appendUint16(static_cast(unit.data)); } - auto loop = instManLocked->getPitchFMLoops(ptNum); - ctr.appendUint16(static_cast(loop.size())); - for (auto& l : loop) { - ctr.appendUint16(static_cast(l.begin)); - ctr.appendUint16(static_cast(l.end)); - ctr.appendUint8(static_cast(l.times)); + auto loops = instManLocked->getPitchFMLoopRoot(ptNum).getAllLoops(); + ctr.appendUint16(static_cast(loops.size())); + for (auto& loop : loops) { + ctr.appendUint16(static_cast(loop.getBeginPos())); + ctr.appendUint16(static_cast(loop.getEndPos())); + ctr.appendUint8(static_cast(loop.getTimes())); } auto release = instManLocked->getPitchFMRelease(ptNum); - switch (release.type) { - case ReleaseType::NoRelease: + switch (release.getType()) { + case InstrumentSequenceRelease::NoRelease: ctr.appendUint8(0x00); break; - case ReleaseType::FixedRelease: + case InstrumentSequenceRelease::FixedRelease: ctr.appendUint8(0x01); - ctr.appendUint16(static_cast(release.begin)); + ctr.appendUint16(static_cast(release.getBeginPos())); break; - case ReleaseType::AbsoluteRelease: + case InstrumentSequenceRelease::AbsoluteRelease: ctr.appendUint8(0x02); - ctr.appendUint16(static_cast(release.begin)); + ctr.appendUint16(static_cast(release.getBeginPos())); break; - case ReleaseType::RelativeRelease: + case InstrumentSequenceRelease::RelativeRelease: ctr.appendUint8(0x03); - ctr.appendUint16(static_cast(release.begin)); + ctr.appendUint16(static_cast(release.getBeginPos())); break; } switch (instManLocked->getPitchFMType(ptNum)) { - case SequenceType::ABSOLUTE_SEQUENCE: ctr.appendUint8(0x00); break; - case SequenceType::RELATIVE_SEQUENCE: ctr.appendUint8(0x02); break; + case SequenceType::AbsoluteSequence: ctr.appendUint8(0x00); break; + case SequenceType::RelativeSequence: ctr.appendUint8(0x02); break; default: break; } ctr.writeUint16(ofs, static_cast(ctr.size() - ofs)); @@ -2063,33 +2038,33 @@ ctr.appendUint16(0); // Dummy offset auto seq = instManLocked->getWaveformSSGSequence(wfNum); ctr.appendUint16(static_cast(seq.size())); - for (auto& com : seq) { - ctr.appendUint16(static_cast(com.type)); - ctr.appendInt32(static_cast(com.data)); - } - auto loop = instManLocked->getWaveformSSGLoops(wfNum); - ctr.appendUint16(static_cast(loop.size())); - for (auto& l : loop) { - ctr.appendUint16(static_cast(l.begin)); - ctr.appendUint16(static_cast(l.end)); - ctr.appendUint8(static_cast(l.times)); + for (auto& unit : seq) { + ctr.appendUint16(static_cast(unit.data)); + ctr.appendInt32(static_cast(unit.subdata)); + } + auto loops = instManLocked->getWaveformSSGLoopRoot(wfNum).getAllLoops(); + ctr.appendUint16(static_cast(loops.size())); + for (auto& loop : loops) { + ctr.appendUint16(static_cast(loop.getBeginPos())); + ctr.appendUint16(static_cast(loop.getEndPos())); + ctr.appendUint8(static_cast(loop.getTimes())); } auto release = instManLocked->getWaveformSSGRelease(wfNum); - switch (release.type) { - case ReleaseType::NoRelease: + switch (release.getType()) { + case InstrumentSequenceRelease::NoRelease: ctr.appendUint8(0x00); break; - case ReleaseType::FixedRelease: + case InstrumentSequenceRelease::FixedRelease: ctr.appendUint8(0x01); - ctr.appendUint16(static_cast(release.begin)); + ctr.appendUint16(static_cast(release.getBeginPos())); break; - case ReleaseType::AbsoluteRelease: + case InstrumentSequenceRelease::AbsoluteRelease: ctr.appendUint8(0x02); - ctr.appendUint16(static_cast(release.begin)); + ctr.appendUint16(static_cast(release.getBeginPos())); break; - case ReleaseType::RelativeRelease: + case InstrumentSequenceRelease::RelativeRelease: ctr.appendUint8(0x03); - ctr.appendUint16(static_cast(release.begin)); + ctr.appendUint16(static_cast(release.getBeginPos())); break; } ctr.appendUint8(0); // Skip sequence type @@ -2104,32 +2079,32 @@ ctr.appendUint16(0); // Dummy offset auto seq = instManLocked->getToneNoiseSSGSequence(tnNum); ctr.appendUint16(static_cast(seq.size())); - for (auto& com : seq) { - ctr.appendUint16(static_cast(com.type)); + for (auto& unit : seq) { + ctr.appendUint16(static_cast(unit.data)); } - auto loop = instManLocked->getToneNoiseSSGLoops(tnNum); - ctr.appendUint16(static_cast(loop.size())); - for (auto& l : loop) { - ctr.appendUint16(static_cast(l.begin)); - ctr.appendUint16(static_cast(l.end)); - ctr.appendUint8(static_cast(l.times)); + auto loops = instManLocked->getToneNoiseSSGLoopRoot(tnNum).getAllLoops(); + ctr.appendUint16(static_cast(loops.size())); + for (auto& loop : loops) { + ctr.appendUint16(static_cast(loop.getBeginPos())); + ctr.appendUint16(static_cast(loop.getEndPos())); + ctr.appendUint8(static_cast(loop.getTimes())); } auto release = instManLocked->getToneNoiseSSGRelease(tnNum); - switch (release.type) { - case ReleaseType::NoRelease: + switch (release.getType()) { + case InstrumentSequenceRelease::NoRelease: ctr.appendUint8(0x00); break; - case ReleaseType::FixedRelease: + case InstrumentSequenceRelease::FixedRelease: ctr.appendUint8(0x01); - ctr.appendUint16(static_cast(release.begin)); + ctr.appendUint16(static_cast(release.getBeginPos())); break; - case ReleaseType::AbsoluteRelease: + case InstrumentSequenceRelease::AbsoluteRelease: ctr.appendUint8(0x02); - ctr.appendUint16(static_cast(release.begin)); + ctr.appendUint16(static_cast(release.getBeginPos())); break; - case ReleaseType::RelativeRelease: + case InstrumentSequenceRelease::RelativeRelease: ctr.appendUint8(0x03); - ctr.appendUint16(static_cast(release.begin)); + ctr.appendUint16(static_cast(release.getBeginPos())); break; } ctr.appendUint8(0); // Skip sequence type @@ -2144,33 +2119,33 @@ ctr.appendUint16(0); // Dummy offset auto seq = instManLocked->getEnvelopeSSGSequence(envNum); ctr.appendUint16(static_cast(seq.size())); - for (auto& com : seq) { - ctr.appendUint16(static_cast(com.type)); - ctr.appendInt32(static_cast(com.data)); - } - auto loop = instManLocked->getEnvelopeSSGLoops(envNum); - ctr.appendUint16(static_cast(loop.size())); - for (auto& l : loop) { - ctr.appendUint16(static_cast(l.begin)); - ctr.appendUint16(static_cast(l.end)); - ctr.appendUint8(static_cast(l.times)); + for (auto& unit : seq) { + ctr.appendUint16(static_cast(unit.data)); + ctr.appendInt32(static_cast(unit.subdata)); + } + auto loops = instManLocked->getEnvelopeSSGLoopRoot(envNum).getAllLoops(); + ctr.appendUint16(static_cast(loops.size())); + for (auto& loop : loops) { + ctr.appendUint16(static_cast(loop.getBeginPos())); + ctr.appendUint16(static_cast(loop.getEndPos())); + ctr.appendUint8(static_cast(loop.getTimes())); } auto release = instManLocked->getEnvelopeSSGRelease(envNum); - switch (release.type) { - case ReleaseType::NoRelease: + switch (release.getType()) { + case InstrumentSequenceRelease::NoRelease: ctr.appendUint8(0x00); break; - case ReleaseType::FixedRelease: + case InstrumentSequenceRelease::FixedRelease: ctr.appendUint8(0x01); - ctr.appendUint16(static_cast(release.begin)); + ctr.appendUint16(static_cast(release.getBeginPos())); break; - case ReleaseType::AbsoluteRelease: + case InstrumentSequenceRelease::AbsoluteRelease: ctr.appendUint8(0x02); - ctr.appendUint16(static_cast(release.begin)); + ctr.appendUint16(static_cast(release.getBeginPos())); break; - case ReleaseType::RelativeRelease: + case InstrumentSequenceRelease::RelativeRelease: ctr.appendUint8(0x03); - ctr.appendUint16(static_cast(release.begin)); + ctr.appendUint16(static_cast(release.getBeginPos())); break; } ctr.appendUint8(0); // Skip sequence type @@ -2185,38 +2160,38 @@ ctr.appendUint16(0); // Dummy offset auto seq = instManLocked->getArpeggioSSGSequence(arpNum); ctr.appendUint16(static_cast(seq.size())); - for (auto& com : seq) { - ctr.appendUint16(static_cast(com.type)); + for (auto& unit : seq) { + ctr.appendUint16(static_cast(unit.data)); } - auto loop = instManLocked->getArpeggioSSGLoops(arpNum); - ctr.appendUint16(static_cast(loop.size())); - for (auto& l : loop) { - ctr.appendUint16(static_cast(l.begin)); - ctr.appendUint16(static_cast(l.end)); - ctr.appendUint8(static_cast(l.times)); + auto loops = instManLocked->getArpeggioSSGLoopRoot(arpNum).getAllLoops(); + ctr.appendUint16(static_cast(loops.size())); + for (auto& loop : loops) { + ctr.appendUint16(static_cast(loop.getBeginPos())); + ctr.appendUint16(static_cast(loop.getEndPos())); + ctr.appendUint8(static_cast(loop.getTimes())); } auto release = instManLocked->getArpeggioSSGRelease(arpNum); - switch (release.type) { - case ReleaseType::NoRelease: + switch (release.getType()) { + case InstrumentSequenceRelease::NoRelease: ctr.appendUint8(0x00); break; - case ReleaseType::FixedRelease: + case InstrumentSequenceRelease::FixedRelease: ctr.appendUint8(0x01); - ctr.appendUint16(static_cast(release.begin)); + ctr.appendUint16(static_cast(release.getBeginPos())); break; - case ReleaseType::AbsoluteRelease: + case InstrumentSequenceRelease::AbsoluteRelease: ctr.appendUint8(0x02); - ctr.appendUint16(static_cast(release.begin)); + ctr.appendUint16(static_cast(release.getBeginPos())); break; - case ReleaseType::RelativeRelease: + case InstrumentSequenceRelease::RelativeRelease: ctr.appendUint8(0x03); - ctr.appendUint16(static_cast(release.begin)); + ctr.appendUint16(static_cast(release.getBeginPos())); break; } switch (instManLocked->getArpeggioSSGType(arpNum)) { - case SequenceType::ABSOLUTE_SEQUENCE: ctr.appendUint8(0x00); break; - case SequenceType::FIXED_SEQUENCE: ctr.appendUint8(0x01); break; - case SequenceType::RELATIVE_SEQUENCE: ctr.appendUint8(0x02); break; + case SequenceType::AbsoluteSequence: ctr.appendUint8(0x00); break; + case SequenceType::FixedSequence: ctr.appendUint8(0x01); break; + case SequenceType::RelativeSequence: ctr.appendUint8(0x02); break; default: break; } ctr.writeUint16(ofs, static_cast(ctr.size() - ofs)); @@ -2230,37 +2205,37 @@ ctr.appendUint16(0); // Dummy offset auto seq = instManLocked->getPitchSSGSequence(ptNum); ctr.appendUint16(static_cast(seq.size())); - for (auto& com : seq) { - ctr.appendUint16(static_cast(com.type)); + for (auto& unit : seq) { + ctr.appendUint16(static_cast(unit.data)); } - auto loop = instManLocked->getPitchSSGLoops(ptNum); - ctr.appendUint16(static_cast(loop.size())); - for (auto& l : loop) { - ctr.appendUint16(static_cast(l.begin)); - ctr.appendUint16(static_cast(l.end)); - ctr.appendUint8(static_cast(l.times)); + auto loops = instManLocked->getPitchSSGLoopRoot(ptNum).getAllLoops(); + ctr.appendUint16(static_cast(loops.size())); + for (auto& loop : loops) { + ctr.appendUint16(static_cast(loop.getBeginPos())); + ctr.appendUint16(static_cast(loop.getEndPos())); + ctr.appendUint8(static_cast(loop.getTimes())); } auto release = instManLocked->getPitchSSGRelease(ptNum); - switch (release.type) { - case ReleaseType::NoRelease: + switch (release.getType()) { + case InstrumentSequenceRelease::NoRelease: ctr.appendUint8(0x00); break; - case ReleaseType::FixedRelease: + case InstrumentSequenceRelease::FixedRelease: ctr.appendUint8(0x01); - ctr.appendUint16(static_cast(release.begin)); + ctr.appendUint16(static_cast(release.getBeginPos())); break; - case ReleaseType::AbsoluteRelease: + case InstrumentSequenceRelease::AbsoluteRelease: ctr.appendUint8(0x02); - ctr.appendUint16(static_cast(release.begin)); + ctr.appendUint16(static_cast(release.getBeginPos())); break; - case ReleaseType::RelativeRelease: + case InstrumentSequenceRelease::RelativeRelease: ctr.appendUint8(0x03); - ctr.appendUint16(static_cast(release.begin)); + ctr.appendUint16(static_cast(release.getBeginPos())); break; } switch (instManLocked->getPitchSSGType(ptNum)) { - case SequenceType::ABSOLUTE_SEQUENCE: ctr.appendUint8(0x00); break; - case SequenceType::RELATIVE_SEQUENCE: ctr.appendUint8(0x02); break; + case SequenceType::AbsoluteSequence: ctr.appendUint8(0x00); break; + case SequenceType::RelativeSequence: ctr.appendUint8(0x02); break; default: break; } ctr.writeUint16(ofs, static_cast(ctr.size() - ofs)); @@ -2294,33 +2269,33 @@ ctr.appendUint16(0); // Dummy offset auto seq = instManLocked->getEnvelopeADPCMSequence(envNum); ctr.appendUint16(static_cast(seq.size())); - for (auto& com : seq) { - ctr.appendUint16(static_cast(com.type)); - ctr.appendInt32(static_cast(com.data)); - } - auto loop = instManLocked->getEnvelopeADPCMLoops(envNum); - ctr.appendUint16(static_cast(loop.size())); - for (auto& l : loop) { - ctr.appendUint16(static_cast(l.begin)); - ctr.appendUint16(static_cast(l.end)); - ctr.appendUint8(static_cast(l.times)); + for (auto& unit : seq) { + ctr.appendUint16(static_cast(unit.data)); + ctr.appendInt32(0); // Dummy set for past format + } + auto loops = instManLocked->getEnvelopeADPCMLoopRoot(envNum).getAllLoops(); + ctr.appendUint16(static_cast(loops.size())); + for (auto& loop : loops) { + ctr.appendUint16(static_cast(loop.getBeginPos())); + ctr.appendUint16(static_cast(loop.getEndPos())); + ctr.appendUint8(static_cast(loop.getTimes())); } auto release = instManLocked->getEnvelopeADPCMRelease(envNum); - switch (release.type) { - case ReleaseType::NoRelease: + switch (release.getType()) { + case InstrumentSequenceRelease::NoRelease: ctr.appendUint8(0x00); break; - case ReleaseType::FixedRelease: + case InstrumentSequenceRelease::FixedRelease: ctr.appendUint8(0x01); - ctr.appendUint16(static_cast(release.begin)); + ctr.appendUint16(static_cast(release.getBeginPos())); break; - case ReleaseType::AbsoluteRelease: + case InstrumentSequenceRelease::AbsoluteRelease: ctr.appendUint8(0x02); - ctr.appendUint16(static_cast(release.begin)); + ctr.appendUint16(static_cast(release.getBeginPos())); break; - case ReleaseType::RelativeRelease: + case InstrumentSequenceRelease::RelativeRelease: ctr.appendUint8(0x03); - ctr.appendUint16(static_cast(release.begin)); + ctr.appendUint16(static_cast(release.getBeginPos())); break; } ctr.appendUint8(0); // Skip sequence type @@ -2335,38 +2310,38 @@ ctr.appendUint16(0); // Dummy offset auto seq = instManLocked->getArpeggioADPCMSequence(arpNum); ctr.appendUint16(static_cast(seq.size())); - for (auto& com : seq) { - ctr.appendUint16(static_cast(com.type)); + for (auto& unit : seq) { + ctr.appendUint16(static_cast(unit.data)); } - auto loop = instManLocked->getArpeggioADPCMLoops(arpNum); - ctr.appendUint16(static_cast(loop.size())); - for (auto& l : loop) { - ctr.appendUint16(static_cast(l.begin)); - ctr.appendUint16(static_cast(l.end)); - ctr.appendUint8(static_cast(l.times)); + auto loops = instManLocked->getArpeggioADPCMLoopRoot(arpNum).getAllLoops(); + ctr.appendUint16(static_cast(loops.size())); + for (auto& loop : loops) { + ctr.appendUint16(static_cast(loop.getBeginPos())); + ctr.appendUint16(static_cast(loop.getEndPos())); + ctr.appendUint8(static_cast(loop.getTimes())); } auto release = instManLocked->getArpeggioADPCMRelease(arpNum); - switch (release.type) { - case ReleaseType::NoRelease: + switch (release.getType()) { + case InstrumentSequenceRelease::NoRelease: ctr.appendUint8(0x00); break; - case ReleaseType::FixedRelease: + case InstrumentSequenceRelease::FixedRelease: ctr.appendUint8(0x01); - ctr.appendUint16(static_cast(release.begin)); + ctr.appendUint16(static_cast(release.getBeginPos())); break; - case ReleaseType::AbsoluteRelease: + case InstrumentSequenceRelease::AbsoluteRelease: ctr.appendUint8(0x02); - ctr.appendUint16(static_cast(release.begin)); + ctr.appendUint16(static_cast(release.getBeginPos())); break; - case ReleaseType::RelativeRelease: + case InstrumentSequenceRelease::RelativeRelease: ctr.appendUint8(0x03); - ctr.appendUint16(static_cast(release.begin)); + ctr.appendUint16(static_cast(release.getBeginPos())); break; } switch (instManLocked->getArpeggioADPCMType(arpNum)) { - case SequenceType::ABSOLUTE_SEQUENCE: ctr.appendUint8(0x00); break; - case SequenceType::FIXED_SEQUENCE: ctr.appendUint8(0x01); break; - case SequenceType::RELATIVE_SEQUENCE: ctr.appendUint8(0x02); break; + case SequenceType::AbsoluteSequence: ctr.appendUint8(0x00); break; + case SequenceType::FixedSequence: ctr.appendUint8(0x01); break; + case SequenceType::RelativeSequence: ctr.appendUint8(0x02); break; default: break; } ctr.writeUint16(ofs, static_cast(ctr.size() - ofs)); @@ -2380,37 +2355,37 @@ ctr.appendUint16(0); // Dummy offset auto seq = instManLocked->getPitchADPCMSequence(ptNum); ctr.appendUint16(static_cast(seq.size())); - for (auto& com : seq) { - ctr.appendUint16(static_cast(com.type)); + for (auto& unit : seq) { + ctr.appendUint16(static_cast(unit.data)); } - auto loop = instManLocked->getPitchADPCMLoops(ptNum); - ctr.appendUint16(static_cast(loop.size())); - for (auto& l : loop) { - ctr.appendUint16(static_cast(l.begin)); - ctr.appendUint16(static_cast(l.end)); - ctr.appendUint8(static_cast(l.times)); + auto loops = instManLocked->getPitchADPCMLoopRoot(ptNum).getAllLoops(); + ctr.appendUint16(static_cast(loops.size())); + for (auto& loop : loops) { + ctr.appendUint16(static_cast(loop.getBeginPos())); + ctr.appendUint16(static_cast(loop.getEndPos())); + ctr.appendUint8(static_cast(loop.getTimes())); } auto release = instManLocked->getPitchADPCMRelease(ptNum); - switch (release.type) { - case ReleaseType::NoRelease: + switch (release.getType()) { + case InstrumentSequenceRelease::NoRelease: ctr.appendUint8(0x00); break; - case ReleaseType::FixedRelease: + case InstrumentSequenceRelease::FixedRelease: ctr.appendUint8(0x01); - ctr.appendUint16(static_cast(release.begin)); + ctr.appendUint16(static_cast(release.getBeginPos())); break; - case ReleaseType::AbsoluteRelease: + case InstrumentSequenceRelease::AbsoluteRelease: ctr.appendUint8(0x02); - ctr.appendUint16(static_cast(release.begin)); + ctr.appendUint16(static_cast(release.getBeginPos())); break; - case ReleaseType::RelativeRelease: + case InstrumentSequenceRelease::RelativeRelease: ctr.appendUint8(0x03); - ctr.appendUint16(static_cast(release.begin)); + ctr.appendUint16(static_cast(release.getBeginPos())); break; } switch (instManLocked->getPitchADPCMType(ptNum)) { - case SequenceType::ABSOLUTE_SEQUENCE: ctr.appendUint8(0x00); break; - case SequenceType::RELATIVE_SEQUENCE: ctr.appendUint8(0x02); break; + case SequenceType::AbsoluteSequence: ctr.appendUint8(0x00); break; + case SequenceType::RelativeSequence: ctr.appendUint8(0x02); break; default: break; } ctr.writeUint16(ofs, static_cast(ctr.size() - ofs)); diff -Nru bambootracker-0.4.5+git20121209/BambooTracker/io/bti_io.hpp bambootracker-0.4.6/BambooTracker/io/bti_io.hpp --- bambootracker-0.4.5+git20121209/BambooTracker/io/bti_io.hpp 2020-12-08 14:05:28.000000000 +0000 +++ bambootracker-0.4.6/BambooTracker/io/bti_io.hpp 2021-02-11 14:37:17.000000000 +0000 @@ -37,11 +37,5 @@ std::weak_ptr instMan, int instNum) const override; void save(BinaryContainer& ctr, const std::weak_ptr instMan, int instNum) const override; - -private: - static size_t loadInstrumentPropertyOperatorSequenceForInstrument( - FMEnvelopeParameter param, size_t instMemCsr, - std::shared_ptr& instManLocked, - const BinaryContainer& ctr, InstrumentFM* inst, int idx, uint32_t version); }; } diff -Nru bambootracker-0.4.5+git20121209/BambooTracker/io/btm_io.cpp bambootracker-0.4.6/BambooTracker/io/btm_io.cpp --- bambootracker-0.4.5+git20121209/BambooTracker/io/btm_io.cpp 2020-12-08 14:05:28.000000000 +0000 +++ bambootracker-0.4.6/BambooTracker/io/btm_io.cpp 2021-02-11 14:37:17.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (C) 2020 Rerrah + * Copyright (C) 2020-2021 Rerrah * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation @@ -26,47 +26,16 @@ #include "btm_io.hpp" #include "file_io_error.hpp" #include "version.hpp" -#include "calc_pitch.hpp" +#include "note.hpp" #include "effect.hpp" #include "io_utils.hpp" namespace io { -BtmIO::BtmIO() : AbstractModuleIO("btm", "BambooTracker module", true, true) {} - -void BtmIO::load(const BinaryContainer& ctr, std::weak_ptr mod, - std::weak_ptr instMan) const +namespace { - size_t globCsr = 0; - if (ctr.readString(globCsr, 16) != "BambooTrackerMod") - throw FileCorruptionError(FileType::Mod, globCsr); - globCsr += 16; - size_t eofOfs = ctr.readUint32(globCsr); - size_t eof = globCsr + eofOfs; - globCsr += 4; - size_t fileVersion = ctr.readUint32(globCsr); - if (fileVersion > Version::ofModuleFileInBCD()) - throw FileVersionError(FileType::Mod); - globCsr += 4; - - while (globCsr < eof) { - if (ctr.readString(globCsr, 8) == "MODULE ") - globCsr = loadModuleSectionInModule(mod, ctr, globCsr + 8, fileVersion); - else if (ctr.readString(globCsr, 8) == "INSTRMNT") - globCsr = loadInstrumentSectionInModule(instMan, ctr, globCsr + 8, fileVersion); - else if (ctr.readString(globCsr, 8) == "INSTPROP") - globCsr = loadInstrumentPropertySectionInModule(instMan, ctr, globCsr + 8, fileVersion); - else if (ctr.readString(globCsr, 8) == "GROOVE ") - globCsr = loadGrooveSectionInModule(mod, ctr, globCsr + 8, fileVersion); - else if (ctr.readString(globCsr, 8) == "SONG ") - globCsr = loadSongSectionInModule(mod, ctr, globCsr + 8, fileVersion); - else - throw FileCorruptionError(FileType::Mod, globCsr); - } -} - -size_t BtmIO::loadModuleSectionInModule(std::weak_ptr mod, const BinaryContainer& ctr, - size_t globCsr, uint32_t version) +size_t loadModuleSection(std::weak_ptr mod, const BinaryContainer& ctr, + size_t globCsr, uint32_t version) { std::shared_ptr modLocked = mod.lock(); @@ -122,9 +91,8 @@ return globCsr + modOfs; } -size_t BtmIO::loadInstrumentSectionInModule(std::weak_ptr instMan, - const BinaryContainer& ctr, size_t globCsr, - uint32_t version) +size_t loadInstrumentSection(std::weak_ptr instMan, + const BinaryContainer& ctr, size_t globCsr, uint32_t version) { std::shared_ptr instManLocked = instMan.lock(); @@ -190,7 +158,7 @@ iCsr += 1; } } - instManLocked->addInstrument(std::unique_ptr(instFM)); + instManLocked->addInstrument(instFM); break; } case 0x01: // SSG @@ -215,8 +183,8 @@ tmp = ctr.readUint8(iCsr); instSSG->setPitchEnabled((0x80 & tmp) ? false : true); instSSG->setPitchNumber(0x7f & tmp); - iCsr += 1; - instManLocked->addInstrument(std::unique_ptr(instSSG)); + /* iCsr += 1; */ + instManLocked->addInstrument(instSSG); break; } case 0x02: // ADPCM @@ -234,8 +202,8 @@ tmp = ctr.readUint8(iCsr); instADPCM->setPitchEnabled((0x80 & tmp) ? false : true); instADPCM->setPitchNumber(0x7f & tmp); - iCsr += 1; - instManLocked->addInstrument(std::unique_ptr(instADPCM)); + /* iCsr += 1; */ + instManLocked->addInstrument(instADPCM); break; } case 0x03: // Drumkit @@ -248,7 +216,7 @@ instKit->setSampleNumber(key, ctr.readUint8(iCsr++)); instKit->setPitch(key, ctr.readInt8(iCsr++)); } - instManLocked->addInstrument(std::unique_ptr(instKit)); + instManLocked->addInstrument(instKit); break; } default: @@ -260,9 +228,63 @@ return globCsr + instOfs; } -size_t BtmIO::loadInstrumentPropertySectionInModule(std::weak_ptr instMan, - const BinaryContainer& ctr, size_t globCsr, - uint32_t version) +size_t loadOperatorSequence(FMEnvelopeParameter param, size_t instMemCsr, + std::shared_ptr& instManLocked, + const BinaryContainer& ctr, uint32_t version) +{ + uint8_t idx = ctr.readUint8(instMemCsr++); + uint16_t ofs = ctr.readUint16(instMemCsr); + size_t csr = instMemCsr + 2; + + uint16_t seqLen = ctr.readUint16(csr); + csr += 2; + for (uint16_t l = 0; l < seqLen; ++l) { + uint16_t data = ctr.readUint16(csr); + csr += 2; + if (version < Version::toBCD(1, 2, 2)) csr += 2; + if (l == 0) + instManLocked->setOperatorSequenceFMSequenceData(param, idx, 0, data); + else + instManLocked->addOperatorSequenceFMSequenceData(param, idx, data); + } + + uint16_t loopCnt = ctr.readUint16(csr); + csr += 2; + for (uint16_t l = 0; l < loopCnt; ++l) { + int begin = ctr.readUint16(csr); + csr += 2; + int end = ctr.readUint16(csr); + csr += 2; + int times = ctr.readUint8(csr++); + instManLocked->addOperatorSequenceFMLoop(param, idx, InstrumentSequenceLoop(begin, end, times)); + } + + switch (ctr.readUint8(csr++)) { + case 0x00: // No release + instManLocked->setOperatorSequenceFMRelease(param, idx, InstrumentSequenceRelease(InstrumentSequenceRelease::NoRelease)); + break; + case 0x01: // Fixed + { + uint16_t pos = ctr.readUint16(csr); + csr += 2; + // Release point check (prevents a bug; see rerrahkr/BambooTracker issue #11) + if (pos < seqLen) instManLocked->setOperatorSequenceFMRelease(param, idx, InstrumentSequenceRelease(InstrumentSequenceRelease::FixedRelease, pos)); + else instManLocked->setOperatorSequenceFMRelease(param, idx, InstrumentSequenceRelease(InstrumentSequenceRelease::NoRelease)); + break; + } + default: + throw FileCorruptionError(FileType::Mod, csr); + } + + if (version >= Version::toBCD(1, 0, 1)) { + ++csr; // Skip sequence type + } + + return ofs + 1; +} + +size_t loadInstrumentPropertySection(std::weak_ptr instMan, + const BinaryContainer& ctr, size_t globCsr, uint32_t version) { std::shared_ptr instManLocked = instMan.lock(); @@ -332,7 +354,7 @@ { uint8_t cnt = ctr.readUint8(instPropCsr++); for (size_t i = 0; i < cnt; ++i) - instPropCsr += loadInstrumentPropertyOperatorSequence( + instPropCsr += loadOperatorSequence( FMEnvelopeParameter::AL, instPropCsr, instManLocked, ctr, version); break; } @@ -340,7 +362,7 @@ { uint8_t cnt = ctr.readUint8(instPropCsr++); for (size_t i = 0; i < cnt; ++i) - instPropCsr += loadInstrumentPropertyOperatorSequence( + instPropCsr += loadOperatorSequence( FMEnvelopeParameter::FB, instPropCsr, instManLocked, ctr, version); break; } @@ -348,7 +370,7 @@ { uint8_t cnt = ctr.readUint8(instPropCsr++); for (size_t i = 0; i < cnt; ++i) - instPropCsr += loadInstrumentPropertyOperatorSequence( + instPropCsr += loadOperatorSequence( FMEnvelopeParameter::AR1, instPropCsr, instManLocked, ctr, version); break; } @@ -356,7 +378,7 @@ { uint8_t cnt = ctr.readUint8(instPropCsr++); for (size_t i = 0; i < cnt; ++i) - instPropCsr += loadInstrumentPropertyOperatorSequence( + instPropCsr += loadOperatorSequence( FMEnvelopeParameter::DR1, instPropCsr, instManLocked, ctr, version); break; } @@ -364,7 +386,7 @@ { uint8_t cnt = ctr.readUint8(instPropCsr++); for (size_t i = 0; i < cnt; ++i) - instPropCsr += loadInstrumentPropertyOperatorSequence( + instPropCsr += loadOperatorSequence( FMEnvelopeParameter::SR1, instPropCsr, instManLocked, ctr, version); break; } @@ -372,7 +394,7 @@ { uint8_t cnt = ctr.readUint8(instPropCsr++); for (size_t i = 0; i < cnt; ++i) - instPropCsr += loadInstrumentPropertyOperatorSequence( + instPropCsr += loadOperatorSequence( FMEnvelopeParameter::RR1, instPropCsr, instManLocked, ctr, version); break; } @@ -380,7 +402,7 @@ { uint8_t cnt = ctr.readUint8(instPropCsr++); for (size_t i = 0; i < cnt; ++i) - instPropCsr += loadInstrumentPropertyOperatorSequence( + instPropCsr += loadOperatorSequence( FMEnvelopeParameter::SL1, instPropCsr, instManLocked, ctr, version); break; } @@ -388,7 +410,7 @@ { uint8_t cnt = ctr.readUint8(instPropCsr++); for (size_t i = 0; i < cnt; ++i) - instPropCsr += loadInstrumentPropertyOperatorSequence( + instPropCsr += loadOperatorSequence( FMEnvelopeParameter::TL1, instPropCsr, instManLocked, ctr, version); break; } @@ -396,7 +418,7 @@ { uint8_t cnt = ctr.readUint8(instPropCsr++); for (size_t i = 0; i < cnt; ++i) - instPropCsr += loadInstrumentPropertyOperatorSequence( + instPropCsr += loadOperatorSequence( FMEnvelopeParameter::KS1, instPropCsr, instManLocked, ctr, version); break; } @@ -404,7 +426,7 @@ { uint8_t cnt = ctr.readUint8(instPropCsr++); for (size_t i = 0; i < cnt; ++i) - instPropCsr += loadInstrumentPropertyOperatorSequence( + instPropCsr += loadOperatorSequence( FMEnvelopeParameter::ML1, instPropCsr, instManLocked, ctr, version); break; } @@ -412,7 +434,7 @@ { uint8_t cnt = ctr.readUint8(instPropCsr++); for (size_t i = 0; i < cnt; ++i) - instPropCsr += loadInstrumentPropertyOperatorSequence( + instPropCsr += loadOperatorSequence( FMEnvelopeParameter::DT1, instPropCsr, instManLocked, ctr, version); break; } @@ -420,7 +442,7 @@ { uint8_t cnt = ctr.readUint8(instPropCsr++); for (size_t i = 0; i < cnt; ++i) - instPropCsr += loadInstrumentPropertyOperatorSequence( + instPropCsr += loadOperatorSequence( FMEnvelopeParameter::AR2, instPropCsr, instManLocked, ctr, version); break; } @@ -428,7 +450,7 @@ { uint8_t cnt = ctr.readUint8(instPropCsr++); for (size_t i = 0; i < cnt; ++i) - instPropCsr += loadInstrumentPropertyOperatorSequence( + instPropCsr += loadOperatorSequence( FMEnvelopeParameter::DR2, instPropCsr, instManLocked, ctr, version); break; } @@ -436,7 +458,7 @@ { uint8_t cnt = ctr.readUint8(instPropCsr++); for (size_t i = 0; i < cnt; ++i) - instPropCsr += loadInstrumentPropertyOperatorSequence( + instPropCsr += loadOperatorSequence( FMEnvelopeParameter::SR2, instPropCsr, instManLocked, ctr, version); break; } @@ -444,7 +466,7 @@ { uint8_t cnt = ctr.readUint8(instPropCsr++); for (size_t i = 0; i < cnt; ++i) - instPropCsr += loadInstrumentPropertyOperatorSequence( + instPropCsr += loadOperatorSequence( FMEnvelopeParameter::RR2, instPropCsr, instManLocked, ctr, version); break; } @@ -452,7 +474,7 @@ { uint8_t cnt = ctr.readUint8(instPropCsr++); for (size_t i = 0; i < cnt; ++i) - instPropCsr += loadInstrumentPropertyOperatorSequence( + instPropCsr += loadOperatorSequence( FMEnvelopeParameter::SL2, instPropCsr, instManLocked, ctr, version); break; } @@ -460,7 +482,7 @@ { uint8_t cnt = ctr.readUint8(instPropCsr++); for (size_t i = 0; i < cnt; ++i) - instPropCsr += loadInstrumentPropertyOperatorSequence( + instPropCsr += loadOperatorSequence( FMEnvelopeParameter::TL2, instPropCsr, instManLocked, ctr, version); break; } @@ -468,7 +490,7 @@ { uint8_t cnt = ctr.readUint8(instPropCsr++); for (size_t i = 0; i < cnt; ++i) - instPropCsr += loadInstrumentPropertyOperatorSequence( + instPropCsr += loadOperatorSequence( FMEnvelopeParameter::KS2, instPropCsr, instManLocked, ctr, version); break; } @@ -476,7 +498,7 @@ { uint8_t cnt = ctr.readUint8(instPropCsr++); for (size_t i = 0; i < cnt; ++i) - instPropCsr += loadInstrumentPropertyOperatorSequence( + instPropCsr += loadOperatorSequence( FMEnvelopeParameter::ML2, instPropCsr, instManLocked, ctr, version); break; } @@ -484,7 +506,7 @@ { uint8_t cnt = ctr.readUint8(instPropCsr++); for (size_t i = 0; i < cnt; ++i) - instPropCsr += loadInstrumentPropertyOperatorSequence( + instPropCsr += loadOperatorSequence( FMEnvelopeParameter::DT2, instPropCsr, instManLocked, ctr, version); break; } @@ -492,7 +514,7 @@ { uint8_t cnt = ctr.readUint8(instPropCsr++); for (size_t i = 0; i < cnt; ++i) - instPropCsr += loadInstrumentPropertyOperatorSequence( + instPropCsr += loadOperatorSequence( FMEnvelopeParameter::AR3, instPropCsr, instManLocked, ctr, version); break; } @@ -500,7 +522,7 @@ { uint8_t cnt = ctr.readUint8(instPropCsr++); for (size_t i = 0; i < cnt; ++i) - instPropCsr += loadInstrumentPropertyOperatorSequence( + instPropCsr += loadOperatorSequence( FMEnvelopeParameter::DR3, instPropCsr, instManLocked, ctr, version); break; } @@ -508,7 +530,7 @@ { uint8_t cnt = ctr.readUint8(instPropCsr++); for (size_t i = 0; i < cnt; ++i) - instPropCsr += loadInstrumentPropertyOperatorSequence( + instPropCsr += loadOperatorSequence( FMEnvelopeParameter::SR3, instPropCsr, instManLocked, ctr, version); break; } @@ -516,7 +538,7 @@ { uint8_t cnt = ctr.readUint8(instPropCsr++); for (size_t i = 0; i < cnt; ++i) - instPropCsr += loadInstrumentPropertyOperatorSequence( + instPropCsr += loadOperatorSequence( FMEnvelopeParameter::RR3, instPropCsr, instManLocked, ctr, version); break; } @@ -524,7 +546,7 @@ { uint8_t cnt = ctr.readUint8(instPropCsr++); for (size_t i = 0; i < cnt; ++i) - instPropCsr += loadInstrumentPropertyOperatorSequence( + instPropCsr += loadOperatorSequence( FMEnvelopeParameter::SL3, instPropCsr, instManLocked, ctr, version); break; } @@ -532,7 +554,7 @@ { uint8_t cnt = ctr.readUint8(instPropCsr++); for (size_t i = 0; i < cnt; ++i) - instPropCsr += loadInstrumentPropertyOperatorSequence( + instPropCsr += loadOperatorSequence( FMEnvelopeParameter::TL3, instPropCsr, instManLocked, ctr, version); break; } @@ -540,7 +562,7 @@ { uint8_t cnt = ctr.readUint8(instPropCsr++); for (size_t i = 0; i < cnt; ++i) - instPropCsr += loadInstrumentPropertyOperatorSequence( + instPropCsr += loadOperatorSequence( FMEnvelopeParameter::KS3, instPropCsr, instManLocked, ctr, version); break; } @@ -548,7 +570,7 @@ { uint8_t cnt = ctr.readUint8(instPropCsr++); for (size_t i = 0; i < cnt; ++i) - instPropCsr += loadInstrumentPropertyOperatorSequence( + instPropCsr += loadOperatorSequence( FMEnvelopeParameter::ML3, instPropCsr, instManLocked, ctr, version); break; } @@ -556,7 +578,7 @@ { uint8_t cnt = ctr.readUint8(instPropCsr++); for (size_t i = 0; i < cnt; ++i) - instPropCsr += loadInstrumentPropertyOperatorSequence( + instPropCsr += loadOperatorSequence( FMEnvelopeParameter::DT3, instPropCsr, instManLocked, ctr, version); break; } @@ -564,7 +586,7 @@ { uint8_t cnt = ctr.readUint8(instPropCsr++); for (size_t i = 0; i < cnt; ++i) - instPropCsr += loadInstrumentPropertyOperatorSequence( + instPropCsr += loadOperatorSequence( FMEnvelopeParameter::AR4, instPropCsr, instManLocked, ctr, version); break; } @@ -572,7 +594,7 @@ { uint8_t cnt = ctr.readUint8(instPropCsr++); for (size_t i = 0; i < cnt; ++i) - instPropCsr += loadInstrumentPropertyOperatorSequence( + instPropCsr += loadOperatorSequence( FMEnvelopeParameter::DR4, instPropCsr, instManLocked, ctr, version); break; } @@ -580,7 +602,7 @@ { uint8_t cnt = ctr.readUint8(instPropCsr++); for (size_t i = 0; i < cnt; ++i) - instPropCsr += loadInstrumentPropertyOperatorSequence( + instPropCsr += loadOperatorSequence( FMEnvelopeParameter::SR4, instPropCsr, instManLocked, ctr, version); break; } @@ -588,7 +610,7 @@ { uint8_t cnt = ctr.readUint8(instPropCsr++); for (size_t i = 0; i < cnt; ++i) - instPropCsr += loadInstrumentPropertyOperatorSequence( + instPropCsr += loadOperatorSequence( FMEnvelopeParameter::RR4, instPropCsr, instManLocked, ctr, version); break; } @@ -596,7 +618,7 @@ { uint8_t cnt = ctr.readUint8(instPropCsr++); for (size_t i = 0; i < cnt; ++i) - instPropCsr += loadInstrumentPropertyOperatorSequence( + instPropCsr += loadOperatorSequence( FMEnvelopeParameter::SL4, instPropCsr, instManLocked, ctr, version); break; } @@ -604,7 +626,7 @@ { uint8_t cnt = ctr.readUint8(instPropCsr++); for (size_t i = 0; i < cnt; ++i) - instPropCsr += loadInstrumentPropertyOperatorSequence( + instPropCsr += loadOperatorSequence( FMEnvelopeParameter::TL4, instPropCsr, instManLocked, ctr, version); break; } @@ -612,7 +634,7 @@ { uint8_t cnt = ctr.readUint8(instPropCsr++); for (size_t i = 0; i < cnt; ++i) - instPropCsr += loadInstrumentPropertyOperatorSequence( + instPropCsr += loadOperatorSequence( FMEnvelopeParameter::KS4, instPropCsr, instManLocked, ctr, version); break; } @@ -620,7 +642,7 @@ { uint8_t cnt = ctr.readUint8(instPropCsr++); for (size_t i = 0; i < cnt; ++i) - instPropCsr += loadInstrumentPropertyOperatorSequence( + instPropCsr += loadOperatorSequence( FMEnvelopeParameter::ML4, instPropCsr, instManLocked, ctr, version); break; } @@ -628,7 +650,7 @@ { uint8_t cnt = ctr.readUint8(instPropCsr++); for (size_t i = 0; i < cnt; ++i) - instPropCsr += loadInstrumentPropertyOperatorSequence( + instPropCsr += loadOperatorSequence( FMEnvelopeParameter::DT4, instPropCsr, instManLocked, ctr, version); break; } @@ -647,27 +669,24 @@ csr += 2; if (version < Version::toBCD(1, 2, 0)) csr += 2; if (l == 0) - instManLocked->setArpeggioFMSequenceCommand(idx, 0, data, 0); + instManLocked->setArpeggioFMSequenceData(idx, 0, data); else - instManLocked->addArpeggioFMSequenceCommand(idx, data, 0); + instManLocked->addArpeggioFMSequenceData(idx, data); } uint16_t loopCnt = ctr.readUint16(csr); csr += 2; - if (loopCnt > 0) { - std::vector begins, ends, times; - for (uint16_t l = 0; l < loopCnt; ++l) { - begins.push_back(ctr.readUint16(csr)); - csr += 2; - ends.push_back(ctr.readUint16(csr)); - csr += 2; - times.push_back(ctr.readUint8(csr++)); - } - instManLocked->setArpeggioFMLoops(idx, begins, ends, times); + for (uint16_t l = 0; l < loopCnt; ++l) { + int begin = ctr.readUint16(csr); + csr += 2; + int end = ctr.readUint16(csr); + csr += 2; + int times = ctr.readUint8(csr++); + instManLocked->addArpeggioFMLoop(idx, InstrumentSequenceLoop(begin, end, times)); } switch (ctr.readUint8(csr++)) { case 0x00: // No release - instManLocked->setArpeggioFMRelease(idx, ReleaseType::NoRelease, -1); + instManLocked->setArpeggioFMRelease(idx, InstrumentSequenceRelease(InstrumentSequenceRelease::NoRelease)); break; case 0x01: // Fixed { @@ -675,8 +694,8 @@ csr += 2; // Release point check (prevents a bug) // https://github.com/rerrahkr/BambooTracker/issues/11 - if (pos < seqLen) instManLocked->setArpeggioFMRelease(idx, ReleaseType::FixedRelease, pos); - else instManLocked->setArpeggioFMRelease(idx, ReleaseType::NoRelease, -1); + if (pos < seqLen) instManLocked->setArpeggioFMRelease(idx, InstrumentSequenceRelease(InstrumentSequenceRelease::FixedRelease, pos)); + else instManLocked->setArpeggioFMRelease(idx, InstrumentSequenceRelease(InstrumentSequenceRelease::NoRelease)); break; } default: @@ -685,19 +704,19 @@ if (version >= Version::toBCD(1, 0, 1)) { switch (ctr.readUint8(csr++)) { case 0x00: // Absolute - instManLocked->setArpeggioFMType(idx, SequenceType::ABSOLUTE_SEQUENCE); + instManLocked->setArpeggioFMType(idx, SequenceType::AbsoluteSequence); break; case 0x01: // Fixed - instManLocked->setArpeggioFMType(idx, SequenceType::FIXED_SEQUENCE); + instManLocked->setArpeggioFMType(idx, SequenceType::FixedSequence); break; case 0x02: // Relative - instManLocked->setArpeggioFMType(idx, SequenceType::RELATIVE_SEQUENCE); + instManLocked->setArpeggioFMType(idx, SequenceType::RelativeSequence); break; default: if (version < Version::toBCD(1, 3, 2)) { // Recover deep clone bug // https://github.com/rerrahkr/BambooTracker/issues/170 - instManLocked->setArpeggioFMType(idx, SequenceType::ABSOLUTE_SEQUENCE); + instManLocked->setArpeggioFMType(idx, SequenceType::AbsoluteSequence); break; } else { @@ -725,28 +744,25 @@ csr += 2; if (version < Version::toBCD(1, 2, 0)) csr += 2; if (l == 0) - instManLocked->setPitchFMSequenceCommand(idx, 0, data, 0); + instManLocked->setPitchFMSequenceData(idx, 0, data); else - instManLocked->addPitchFMSequenceCommand(idx, data, 0); + instManLocked->addPitchFMSequenceData(idx, data); } uint16_t loopCnt = ctr.readUint16(csr); csr += 2; - if (loopCnt > 0) { - std::vector begins, ends, times; - for (uint16_t l = 0; l < loopCnt; ++l) { - begins.push_back(ctr.readUint16(csr)); - csr += 2; - ends.push_back(ctr.readUint16(csr)); - csr += 2; - times.push_back(ctr.readUint8(csr++)); - } - instManLocked->setPitchFMLoops(idx, begins, ends, times); + for (uint16_t l = 0; l < loopCnt; ++l) { + int begin = ctr.readUint16(csr); + csr += 2; + int end = ctr.readUint16(csr); + csr += 2; + int times = ctr.readUint8(csr++); + instManLocked->addPitchFMLoop(idx, InstrumentSequenceLoop(begin, end, times)); } switch (ctr.readUint8(csr++)) { case 0x00: // No release - instManLocked->setPitchFMRelease(idx, ReleaseType::NoRelease, -1); + instManLocked->setPitchFMRelease(idx, InstrumentSequenceRelease(InstrumentSequenceRelease::NoRelease)); break; case 0x01: // Fixed { @@ -754,8 +770,8 @@ csr += 2; // Release point check (prevents a bug) // https://github.com/rerrahkr/BambooTracker/issues/11 - if (pos < seqLen) instManLocked->setPitchFMRelease(idx, ReleaseType::FixedRelease, pos); - else instManLocked->setPitchFMRelease(idx, ReleaseType::NoRelease, -1); + if (pos < seqLen) instManLocked->setPitchFMRelease(idx, InstrumentSequenceRelease(InstrumentSequenceRelease::FixedRelease, pos)); + else instManLocked->setPitchFMRelease(idx, InstrumentSequenceRelease(InstrumentSequenceRelease::NoRelease)); break; } default: @@ -764,16 +780,16 @@ if (version >= Version::toBCD(1, 0, 1)) { switch (ctr.readUint8(csr++)) { case 0x00: // Absolute - instManLocked->setPitchFMType(idx, SequenceType::ABSOLUTE_SEQUENCE); + instManLocked->setPitchFMType(idx, SequenceType::AbsoluteSequence); break; case 0x02: // Relative - instManLocked->setPitchFMType(idx, SequenceType::RELATIVE_SEQUENCE); + instManLocked->setPitchFMType(idx, SequenceType::RelativeSequence); break; default: if (version < Version::toBCD(1, 3, 2)) { // Recover deep clone bug // https://github.com/rerrahkr/BambooTracker/issues/170 - instManLocked->setPitchFMType(idx, SequenceType::ABSOLUTE_SEQUENCE); + instManLocked->setPitchFMType(idx, SequenceType::AbsoluteSequence); break; } else { @@ -800,8 +816,8 @@ uint16_t data = ctr.readUint16(csr); csr += 2; if (version < Version::toBCD(1, 2, 0)) { - if (data == 3) data = static_cast(SSGWaveformType::SQM_TRIANGLE); - else if (data == 4) data = static_cast(SSGWaveformType::SQM_SAW); + if (data == 3) data = SSGWaveformType::SQM_TRIANGLE; + else if (data == 4) data = SSGWaveformType::SQM_SAW; } int32_t subdata; if (version >= Version::toBCD(1, 2, 0)) { @@ -812,31 +828,39 @@ subdata = ctr.readUint16(csr); csr += 2; if (subdata != -1) - subdata = calc_pitch::calculateSSGSquareTP(subdata); + subdata = note_utils::calculateSSGSquareTP(subdata, 0); + } + SSGWaveformUnit unit; + switch (data) { + case SSGWaveformType::SQM_TRIANGLE: + case SSGWaveformType::SQM_SAW: + case SSGWaveformType::SQM_INVSAW: + unit = SSGWaveformUnit::makeUnitWithDecode(data, subdata); + break; + default: + unit = SSGWaveformUnit::makeOnlyDataUnit(data); + break; } if (l == 0) - instManLocked->setWaveformSSGSequenceCommand(idx, 0, data, subdata); + instManLocked->setWaveformSSGSequenceData(idx, 0, unit); else - instManLocked->addWaveformSSGSequenceCommand(idx, data, subdata); + instManLocked->addWaveformSSGSequenceData(idx, unit); } uint16_t loopCnt = ctr.readUint16(csr); csr += 2; - if (loopCnt > 0) { - std::vector begins, ends, times; - for (uint16_t l = 0; l < loopCnt; ++l) { - begins.push_back(ctr.readUint16(csr)); - csr += 2; - ends.push_back(ctr.readUint16(csr)); - csr += 2; - times.push_back(ctr.readUint8(csr++)); - } - instManLocked->setWaveformSSGLoops(idx, begins, ends, times); + for (uint16_t l = 0; l < loopCnt; ++l) { + int begin = ctr.readUint16(csr); + csr += 2; + int end = ctr.readUint16(csr); + csr += 2; + int times = ctr.readUint8(csr++); + instManLocked->addWaveformSSGLoop(idx, InstrumentSequenceLoop(begin, end, times)); } switch (ctr.readUint8(csr++)) { case 0x00: // No release - instManLocked->setWaveformSSGRelease(idx, ReleaseType::NoRelease, -1); + instManLocked->setWaveformSSGRelease(idx, InstrumentSequenceRelease(InstrumentSequenceRelease::NoRelease)); break; case 0x01: // Fixed { @@ -844,8 +868,8 @@ csr += 2; // Release point check (prevents a bug) // https://github.com/rerrahkr/BambooTracker/issues/11 - if (pos < seqLen) instManLocked->setWaveformSSGRelease(idx, ReleaseType::FixedRelease, pos); - else instManLocked->setWaveformSSGRelease(idx, ReleaseType::NoRelease, -1); + if (pos < seqLen) instManLocked->setWaveformSSGRelease(idx, InstrumentSequenceRelease(InstrumentSequenceRelease::FixedRelease, pos)); + else instManLocked->setWaveformSSGRelease(idx, InstrumentSequenceRelease(InstrumentSequenceRelease::NoRelease)); break; } default: @@ -880,28 +904,25 @@ } if (version < Version::toBCD(1, 2, 0)) csr += 2; if (l == 0) - instManLocked->setToneNoiseSSGSequenceCommand(idx, 0, data, 0); + instManLocked->setToneNoiseSSGSequenceData(idx, 0, data); else - instManLocked->addToneNoiseSSGSequenceCommand(idx, data, 0); + instManLocked->addToneNoiseSSGSequenceData(idx, data); } uint16_t loopCnt = ctr.readUint16(csr); csr += 2; - if (loopCnt > 0) { - std::vector begins, ends, times; - for (uint16_t l = 0; l < loopCnt; ++l) { - begins.push_back(ctr.readUint16(csr)); - csr += 2; - ends.push_back(ctr.readUint16(csr)); - csr += 2; - times.push_back(ctr.readUint8(csr++)); - } - instManLocked->setToneNoiseSSGLoops(idx, begins, ends, times); + for (uint16_t l = 0; l < loopCnt; ++l) { + int begin = ctr.readUint16(csr); + csr += 2; + int end = ctr.readUint16(csr); + csr += 2; + int times = ctr.readUint8(csr++); + instManLocked->addToneNoiseSSGLoop(idx, InstrumentSequenceLoop(begin, end, times)); } switch (ctr.readUint8(csr++)) { case 0x00: // No release - instManLocked->setToneNoiseSSGRelease(idx, ReleaseType::NoRelease, -1); + instManLocked->setToneNoiseSSGRelease(idx, InstrumentSequenceRelease(InstrumentSequenceRelease::NoRelease)); break; case 0x01: // Fixed { @@ -909,8 +930,8 @@ csr += 2; // Release point check (prevents a bug) // https://github.com/rerrahkr/BambooTracker/issues/11 - if (pos < seqLen) instManLocked->setToneNoiseSSGRelease(idx, ReleaseType::FixedRelease, pos); - else instManLocked->setToneNoiseSSGRelease(idx, ReleaseType::NoRelease, -1); + if (pos < seqLen) instManLocked->setToneNoiseSSGRelease(idx, InstrumentSequenceRelease(InstrumentSequenceRelease::FixedRelease, pos)); + else instManLocked->setToneNoiseSSGRelease(idx, InstrumentSequenceRelease(InstrumentSequenceRelease::NoRelease)); break; } default: @@ -946,29 +967,28 @@ subdata = ctr.readUint16(csr); csr += 2; } + SSGEnvelopeUnit unit = (data < 16) ? SSGEnvelopeUnit::makeOnlyDataUnit(data) + : SSGEnvelopeUnit::makeUnitWithDecode(data, subdata); if (l == 0) - instManLocked->setEnvelopeSSGSequenceCommand(idx, 0, data, subdata); + instManLocked->setEnvelopeSSGSequenceData(idx, 0, unit); else - instManLocked->addEnvelopeSSGSequenceCommand(idx, data, subdata); + instManLocked->addEnvelopeSSGSequenceData(idx, unit); } uint16_t loopCnt = ctr.readUint16(csr); csr += 2; - if (loopCnt > 0) { - std::vector begins, ends, times; - for (uint16_t l = 0; l < loopCnt; ++l) { - begins.push_back(ctr.readUint16(csr)); - csr += 2; - ends.push_back(ctr.readUint16(csr)); - csr += 2; - times.push_back(ctr.readUint8(csr++)); - } - instManLocked->setEnvelopeSSGLoops(idx, begins, ends, times); + for (uint16_t l = 0; l < loopCnt; ++l) { + int begin = ctr.readUint16(csr); + csr += 2; + int end = ctr.readUint16(csr); + csr += 2; + int times = ctr.readUint8(csr++); + instManLocked->addEnvelopeSSGLoop(idx, InstrumentSequenceLoop(begin, end, times)); } switch (ctr.readUint8(csr++)) { case 0x00: // No release - instManLocked->setEnvelopeSSGRelease(idx, ReleaseType::NoRelease, -1); + instManLocked->setEnvelopeSSGRelease(idx, InstrumentSequenceRelease(InstrumentSequenceRelease::NoRelease)); break; // Release point check (prevents a bug) // https://github.com/rerrahkr/BambooTracker/issues/11 @@ -976,24 +996,24 @@ { uint16_t pos = ctr.readUint16(csr); csr += 2; - if (pos < seqLen) instManLocked->setEnvelopeSSGRelease(idx, ReleaseType::FixedRelease, pos); - else instManLocked->setEnvelopeSSGRelease(idx, ReleaseType::NoRelease, -1); + if (pos < seqLen) instManLocked->setEnvelopeSSGRelease(idx, InstrumentSequenceRelease(InstrumentSequenceRelease::FixedRelease, pos)); + else instManLocked->setEnvelopeSSGRelease(idx, InstrumentSequenceRelease(InstrumentSequenceRelease::NoRelease)); break; } case 0x02: // Absolute { uint16_t pos = ctr.readUint16(csr); csr += 2; - if (pos < seqLen) instManLocked->setEnvelopeSSGRelease(idx, ReleaseType::AbsoluteRelease, pos); - else instManLocked->setEnvelopeSSGRelease(idx, ReleaseType::NoRelease, -1); + if (pos < seqLen) instManLocked->setEnvelopeSSGRelease(idx, InstrumentSequenceRelease(InstrumentSequenceRelease::AbsoluteRelease, pos)); + else instManLocked->setEnvelopeSSGRelease(idx, InstrumentSequenceRelease(InstrumentSequenceRelease::NoRelease)); break; } case 0x03: // Relative { uint16_t pos = ctr.readUint16(csr); csr += 2; - if (pos < seqLen) instManLocked->setEnvelopeSSGRelease(idx, ReleaseType::RelativeRelease, pos); - else instManLocked->setEnvelopeSSGRelease(idx, ReleaseType::NoRelease, -1); + if (pos < seqLen) instManLocked->setEnvelopeSSGRelease(idx, InstrumentSequenceRelease(InstrumentSequenceRelease::RelativeRelease, pos)); + else instManLocked->setEnvelopeSSGRelease(idx, InstrumentSequenceRelease(InstrumentSequenceRelease::NoRelease)); break; } default: @@ -1022,28 +1042,25 @@ csr += 2; if (version < Version::toBCD(1, 2, 0)) csr += 2; if (l == 0) - instManLocked->setArpeggioSSGSequenceCommand(idx, 0, data, 0); + instManLocked->setArpeggioSSGSequenceData(idx, 0, data); else - instManLocked->addArpeggioSSGSequenceCommand(idx, data, 0); + instManLocked->addArpeggioSSGSequenceData(idx, data); } uint16_t loopCnt = ctr.readUint16(csr); csr += 2; - if (loopCnt > 0) { - std::vector begins, ends, times; - for (uint16_t l = 0; l < loopCnt; ++l) { - begins.push_back(ctr.readUint16(csr)); - csr += 2; - ends.push_back(ctr.readUint16(csr)); - csr += 2; - times.push_back(ctr.readUint8(csr++)); - } - instManLocked->setArpeggioSSGLoops(idx, begins, ends, times); + for (uint16_t l = 0; l < loopCnt; ++l) { + int begin = ctr.readUint16(csr); + csr += 2; + int end = ctr.readUint16(csr); + csr += 2; + int times = ctr.readUint8(csr++); + instManLocked->addArpeggioSSGLoop(idx, InstrumentSequenceLoop(begin, end, times)); } switch (ctr.readUint8(csr++)) { case 0x00: // No release - instManLocked->setArpeggioSSGRelease(idx, ReleaseType::NoRelease, -1); + instManLocked->setArpeggioSSGRelease(idx, InstrumentSequenceRelease(InstrumentSequenceRelease::NoRelease)); break; case 0x01: // Fixed { @@ -1051,8 +1068,8 @@ csr += 2; // Release point check (prevents a bug) // https://github.com/rerrahkr/BambooTracker/issues/11 - if (pos < seqLen) instManLocked->setArpeggioSSGRelease(idx, ReleaseType::FixedRelease, pos); - else instManLocked->setArpeggioSSGRelease(idx, ReleaseType::NoRelease, -1); + if (pos < seqLen) instManLocked->setArpeggioSSGRelease(idx, InstrumentSequenceRelease(InstrumentSequenceRelease::FixedRelease, pos)); + else instManLocked->setArpeggioSSGRelease(idx, InstrumentSequenceRelease(InstrumentSequenceRelease::NoRelease)); break; } default: @@ -1061,19 +1078,19 @@ if (version >= Version::toBCD(1, 0, 1)) { switch (ctr.readUint8(csr++)) { case 0x00: // Absolute - instManLocked->setArpeggioSSGType(idx, SequenceType::ABSOLUTE_SEQUENCE); + instManLocked->setArpeggioSSGType(idx, SequenceType::AbsoluteSequence); break; case 0x01: // Fixed - instManLocked->setArpeggioSSGType(idx, SequenceType::FIXED_SEQUENCE); + instManLocked->setArpeggioSSGType(idx, SequenceType::FixedSequence); break; case 0x02: // Relative - instManLocked->setArpeggioSSGType(idx, SequenceType::RELATIVE_SEQUENCE); + instManLocked->setArpeggioSSGType(idx, SequenceType::RelativeSequence); break; default: if (version < Version::toBCD(1, 3, 2)) { // Recover deep clone bug // https://github.com/rerrahkr/BambooTracker/issues/170 - instManLocked->setArpeggioSSGType(idx, SequenceType::ABSOLUTE_SEQUENCE); + instManLocked->setArpeggioSSGType(idx, SequenceType::AbsoluteSequence); break; } else { @@ -1101,28 +1118,25 @@ csr += 2; if (version < Version::toBCD(1, 2, 0)) csr += 2; if (l == 0) - instManLocked->setPitchSSGSequenceCommand(idx, 0, data, 0); + instManLocked->setPitchSSGSequenceData(idx, 0, data); else - instManLocked->addPitchSSGSequenceCommand(idx, data, 0); + instManLocked->addPitchSSGSequenceData(idx, data); } uint16_t loopCnt = ctr.readUint16(csr); csr += 2; - if (loopCnt > 0) { - std::vector begins, ends, times; - for (uint16_t l = 0; l < loopCnt; ++l) { - begins.push_back(ctr.readUint16(csr)); - csr += 2; - ends.push_back(ctr.readUint16(csr)); - csr += 2; - times.push_back(ctr.readUint8(csr++)); - } - instManLocked->setPitchSSGLoops(idx, begins, ends, times); + for (uint16_t l = 0; l < loopCnt; ++l) { + int begin = ctr.readUint16(csr); + csr += 2; + int end = ctr.readUint16(csr); + csr += 2; + int times = ctr.readUint8(csr++); + instManLocked->addPitchSSGLoop(idx, InstrumentSequenceLoop(begin, end, times)); } switch (ctr.readUint8(csr++)) { case 0x00: // No release - instManLocked->setPitchSSGRelease(idx, ReleaseType::NoRelease, -1); + instManLocked->setPitchSSGRelease(idx, InstrumentSequenceRelease(InstrumentSequenceRelease::NoRelease)); break; case 0x01: // Fixed { @@ -1130,8 +1144,8 @@ csr += 2; // Release point check (prevents a bug) // https://github.com/rerrahkr/BambooTracker/issues/11 - if (pos < seqLen) instManLocked->setPitchSSGRelease(idx, ReleaseType::FixedRelease, pos); - else instManLocked->setPitchSSGRelease(idx, ReleaseType::NoRelease, -1); + if (pos < seqLen) instManLocked->setPitchSSGRelease(idx, InstrumentSequenceRelease(InstrumentSequenceRelease::FixedRelease, pos)); + else instManLocked->setPitchSSGRelease(idx, InstrumentSequenceRelease(InstrumentSequenceRelease::NoRelease)); break; } default: @@ -1140,16 +1154,16 @@ if (version >= Version::toBCD(1, 0, 1)) { switch (ctr.readUint8(csr++)) { case 0x00: // Absolute - instManLocked->setPitchSSGType(idx, SequenceType::ABSOLUTE_SEQUENCE); + instManLocked->setPitchSSGType(idx, SequenceType::AbsoluteSequence); break; case 0x02: // Relative - instManLocked->setPitchSSGType(idx, SequenceType::RELATIVE_SEQUENCE); + instManLocked->setPitchSSGType(idx, SequenceType::RelativeSequence); break; default: if (version < Version::toBCD(1, 3, 2)) { // Recover deep clone bug // https://github.com/rerrahkr/BambooTracker/issues/170 - instManLocked->setPitchSSGType(idx, SequenceType::ABSOLUTE_SEQUENCE); + instManLocked->setPitchSSGType(idx, SequenceType::AbsoluteSequence); break; } else { @@ -1177,7 +1191,7 @@ uint32_t len = ctr.readUint32(csr); csr += 4; std::vector samples = ctr.getSubcontainer(csr, len).toVector(); - csr += len; + /* csr += len; */ instManLocked->storeSampleADPCMRawSample(idx, std::move(samples)); instPropCsr += ofs; @@ -1196,33 +1210,27 @@ csr += 2; for (uint16_t l = 0; l < seqLen; ++l) { uint16_t data = ctr.readUint16(csr); - csr += 2; - int32_t subdata; - subdata = ctr.readInt32(csr); - csr += 4; + csr += 6; // Skip subdata if (l == 0) - instManLocked->setEnvelopeADPCMSequenceCommand(idx, 0, data, subdata); + instManLocked->setEnvelopeADPCMSequenceData(idx, 0, data); else - instManLocked->addEnvelopeADPCMSequenceCommand(idx, data, subdata); + instManLocked->addEnvelopeADPCMSequenceData(idx, data); } uint16_t loopCnt = ctr.readUint16(csr); csr += 2; - if (loopCnt > 0) { - std::vector begins, ends, times; - for (uint16_t l = 0; l < loopCnt; ++l) { - begins.push_back(ctr.readUint16(csr)); - csr += 2; - ends.push_back(ctr.readUint16(csr)); - csr += 2; - times.push_back(ctr.readUint8(csr++)); - } - instManLocked->setEnvelopeADPCMLoops(idx, begins, ends, times); + for (uint16_t l = 0; l < loopCnt; ++l) { + int begin = ctr.readUint16(csr); + csr += 2; + int end = ctr.readUint16(csr); + csr += 2; + int times = ctr.readUint8(csr++); + instManLocked->addEnvelopeADPCMLoop(idx, InstrumentSequenceLoop(begin, end, times)); } switch (ctr.readUint8(csr++)) { case 0x00: // No release - instManLocked->setEnvelopeADPCMRelease(idx, ReleaseType::NoRelease, -1); + instManLocked->setEnvelopeADPCMRelease(idx, InstrumentSequenceRelease(InstrumentSequenceRelease::NoRelease)); break; // Release point check (prevents a bug) // https://github.com/rerrahkr/BambooTracker/issues/11 @@ -1230,24 +1238,24 @@ { uint16_t pos = ctr.readUint16(csr); csr += 2; - if (pos < seqLen) instManLocked->setEnvelopeADPCMRelease(idx, ReleaseType::FixedRelease, pos); - else instManLocked->setEnvelopeADPCMRelease(idx, ReleaseType::NoRelease, -1); + if (pos < seqLen) instManLocked->setEnvelopeADPCMRelease(idx, InstrumentSequenceRelease(InstrumentSequenceRelease::FixedRelease, pos)); + else instManLocked->setEnvelopeADPCMRelease(idx, InstrumentSequenceRelease(InstrumentSequenceRelease::NoRelease)); break; } case 0x02: // Absolute { uint16_t pos = ctr.readUint16(csr); csr += 2; - if (pos < seqLen) instManLocked->setEnvelopeADPCMRelease(idx, ReleaseType::AbsoluteRelease, pos); - else instManLocked->setEnvelopeADPCMRelease(idx, ReleaseType::NoRelease, -1); + if (pos < seqLen) instManLocked->setEnvelopeADPCMRelease(idx, InstrumentSequenceRelease(InstrumentSequenceRelease::AbsoluteRelease, pos)); + else instManLocked->setEnvelopeADPCMRelease(idx, InstrumentSequenceRelease(InstrumentSequenceRelease::NoRelease)); break; } case 0x03: // Relative { uint16_t pos = ctr.readUint16(csr); csr += 2; - if (pos < seqLen) instManLocked->setEnvelopeADPCMRelease(idx, ReleaseType::RelativeRelease, pos); - else instManLocked->setEnvelopeADPCMRelease(idx, ReleaseType::NoRelease, -1); + if (pos < seqLen) instManLocked->setEnvelopeADPCMRelease(idx, InstrumentSequenceRelease(InstrumentSequenceRelease::RelativeRelease, pos)); + else instManLocked->setEnvelopeADPCMRelease(idx, InstrumentSequenceRelease(InstrumentSequenceRelease::NoRelease)); break; } default: @@ -1273,28 +1281,25 @@ uint16_t data = ctr.readUint16(csr); csr += 2; if (l == 0) - instManLocked->setArpeggioADPCMSequenceCommand(idx, 0, data, 0); + instManLocked->setArpeggioADPCMSequenceData(idx, 0, data); else - instManLocked->addArpeggioADPCMSequenceCommand(idx, data, 0); + instManLocked->addArpeggioADPCMSequenceData(idx, data); } uint16_t loopCnt = ctr.readUint16(csr); csr += 2; - if (loopCnt > 0) { - std::vector begins, ends, times; - for (uint16_t l = 0; l < loopCnt; ++l) { - begins.push_back(ctr.readUint16(csr)); - csr += 2; - ends.push_back(ctr.readUint16(csr)); - csr += 2; - times.push_back(ctr.readUint8(csr++)); - } - instManLocked->setArpeggioADPCMLoops(idx, begins, ends, times); + for (uint16_t l = 0; l < loopCnt; ++l) { + int begin = ctr.readUint16(csr); + csr += 2; + int end = ctr.readUint16(csr); + csr += 2; + int times = ctr.readUint8(csr++); + instManLocked->addArpeggioADPCMLoop(idx, InstrumentSequenceLoop(begin, end, times)); } switch (ctr.readUint8(csr++)) { case 0x00: // No release - instManLocked->setArpeggioADPCMRelease(idx, ReleaseType::NoRelease, -1); + instManLocked->setArpeggioADPCMRelease(idx, InstrumentSequenceRelease(InstrumentSequenceRelease::NoRelease)); break; case 0x01: // Fixed { @@ -1302,8 +1307,8 @@ csr += 2; // Release point check (prevents a bug) // https://github.com/rerrahkr/BambooTracker/issues/11 - if (pos < seqLen) instManLocked->setArpeggioADPCMRelease(idx, ReleaseType::FixedRelease, pos); - else instManLocked->setArpeggioADPCMRelease(idx, ReleaseType::NoRelease, -1); + if (pos < seqLen) instManLocked->setArpeggioADPCMRelease(idx, InstrumentSequenceRelease(InstrumentSequenceRelease::FixedRelease, pos)); + else instManLocked->setArpeggioADPCMRelease(idx, InstrumentSequenceRelease(InstrumentSequenceRelease::NoRelease)); break; } default: @@ -1311,13 +1316,13 @@ } switch (ctr.readUint8(csr++)) { case 0x00: // Absolute - instManLocked->setArpeggioADPCMType(idx, SequenceType::ABSOLUTE_SEQUENCE); + instManLocked->setArpeggioADPCMType(idx, SequenceType::AbsoluteSequence); break; case 0x01: // Fixed - instManLocked->setArpeggioADPCMType(idx, SequenceType::FIXED_SEQUENCE); + instManLocked->setArpeggioADPCMType(idx, SequenceType::FixedSequence); break; case 0x02: // Relative - instManLocked->setArpeggioADPCMType(idx, SequenceType::RELATIVE_SEQUENCE); + instManLocked->setArpeggioADPCMType(idx, SequenceType::RelativeSequence); break; default: throw FileCorruptionError(FileType::Mod, csr); @@ -1341,28 +1346,25 @@ uint16_t data = ctr.readUint16(csr); csr += 2; if (l == 0) - instManLocked->setPitchADPCMSequenceCommand(idx, 0, data, 0); + instManLocked->setPitchADPCMSequenceData(idx, 0, data); else - instManLocked->addPitchADPCMSequenceCommand(idx, data, 0); + instManLocked->addPitchADPCMSequenceData(idx, data); } uint16_t loopCnt = ctr.readUint16(csr); csr += 2; - if (loopCnt > 0) { - std::vector begins, ends, times; - for (uint16_t l = 0; l < loopCnt; ++l) { - begins.push_back(ctr.readUint16(csr)); - csr += 2; - ends.push_back(ctr.readUint16(csr)); - csr += 2; - times.push_back(ctr.readUint8(csr++)); - } - instManLocked->setPitchADPCMLoops(idx, begins, ends, times); + for (uint16_t l = 0; l < loopCnt; ++l) { + int begin = ctr.readUint16(csr); + csr += 2; + int end = ctr.readUint16(csr); + csr += 2; + int times = ctr.readUint8(csr++); + instManLocked->addPitchADPCMLoop(idx, InstrumentSequenceLoop(begin, end, times)); } switch (ctr.readUint8(csr++)) { case 0x00: // No release - instManLocked->setPitchADPCMRelease(idx, ReleaseType::NoRelease, -1); + instManLocked->setPitchADPCMRelease(idx, InstrumentSequenceRelease(InstrumentSequenceRelease::NoRelease)); break; case 0x01: // Fixed { @@ -1370,8 +1372,8 @@ csr += 2; // Release point check (prevents a bug) // https://github.com/rerrahkr/BambooTracker/issues/11 - if (pos < seqLen) instManLocked->setPitchADPCMRelease(idx, ReleaseType::FixedRelease, pos); - else instManLocked->setPitchADPCMRelease(idx, ReleaseType::NoRelease, -1); + if (pos < seqLen) instManLocked->setPitchADPCMRelease(idx, InstrumentSequenceRelease(InstrumentSequenceRelease::FixedRelease, pos)); + else instManLocked->setPitchADPCMRelease(idx, InstrumentSequenceRelease(InstrumentSequenceRelease::NoRelease)); break; } default: @@ -1379,10 +1381,10 @@ } switch (ctr.readUint8(csr++)) { case 0x00: // Absolute - instManLocked->setPitchADPCMType(idx, SequenceType::ABSOLUTE_SEQUENCE); + instManLocked->setPitchADPCMType(idx, SequenceType::AbsoluteSequence); break; case 0x02: // Relative - instManLocked->setPitchADPCMType(idx, SequenceType::RELATIVE_SEQUENCE); + instManLocked->setPitchADPCMType(idx, SequenceType::RelativeSequence); break; default: throw FileCorruptionError(FileType::Mod, csr); @@ -1400,67 +1402,8 @@ return globCsr; } -size_t BtmIO::loadInstrumentPropertyOperatorSequence(FMEnvelopeParameter param, - size_t instMemCsr, - std::shared_ptr& instManLocked, - const BinaryContainer& ctr, uint32_t version) -{ - uint8_t idx = ctr.readUint8(instMemCsr++); - uint16_t ofs = ctr.readUint16(instMemCsr); - size_t csr = instMemCsr + 2; - - uint16_t seqLen = ctr.readUint16(csr); - csr += 2; - for (uint16_t l = 0; l < seqLen; ++l) { - uint16_t data = ctr.readUint16(csr); - csr += 2; - if (version < Version::toBCD(1, 2, 2)) csr += 2; - if (l == 0) - instManLocked->setOperatorSequenceFMSequenceCommand(param, idx, 0, data, 0); - else - instManLocked->addOperatorSequenceFMSequenceCommand(param, idx, data, 0); - } - - uint16_t loopCnt = ctr.readUint16(csr); - csr += 2; - if (loopCnt > 0) { - std::vector begins, ends, times; - for (uint16_t l = 0; l < loopCnt; ++l) { - begins.push_back(ctr.readUint16(csr)); - csr += 2; - ends.push_back(ctr.readUint16(csr)); - csr += 2; - times.push_back(ctr.readUint8(csr++)); - } - instManLocked->setOperatorSequenceFMLoops(param, idx, begins, ends, times); - } - - switch (ctr.readUint8(csr++)) { - case 0x00: // No release - instManLocked->setOperatorSequenceFMRelease(param, idx, ReleaseType::NoRelease, -1); - break; - case 0x01: // Fixed - { - uint16_t pos = ctr.readUint16(csr); - csr += 2; - // Release point check (prevents a bug; see rerrahkr/BambooTracker issue #11) - if (pos < seqLen) instManLocked->setOperatorSequenceFMRelease(param, idx, ReleaseType::FixedRelease, pos); - else instManLocked->setOperatorSequenceFMRelease(param, idx, ReleaseType::NoRelease, -1); - break; - } - default: - throw FileCorruptionError(FileType::Mod, csr); - } - - if (version >= Version::toBCD(1, 0, 1)) { - ++csr; // Skip sequence type - } - - return ofs + 1; -} - -size_t BtmIO::loadGrooveSectionInModule(std::weak_ptr mod, const BinaryContainer& ctr, - size_t globCsr, uint32_t version) +size_t loadGrooveSection(std::weak_ptr mod, const BinaryContainer& ctr, + size_t globCsr, uint32_t version) { (void)version; @@ -1484,8 +1427,8 @@ return globCsr + grvOfs; } -size_t BtmIO::loadSongSectionInModule(std::weak_ptr mod, const BinaryContainer& ctr, - size_t globCsr, uint32_t version) +size_t loadSongSection(std::weak_ptr mod, const BinaryContainer& ctr, + size_t globCsr, uint32_t version) { std::shared_ptr modLocked = mod.lock(); @@ -1590,8 +1533,8 @@ EffectType efftype = EffectType::NoEffect; if (eventFlag & 0x0008) { std::string id = ctr.readString(pcsr, 2); - step.setEffectID(0, id); - efftype = Effect::toEffectType(sndsrc, id); + step.setEffectId(0, id); + efftype = effect_utils::validateEffectId(sndsrc, id); pcsr += 2; } if (eventFlag & 0x0010) { @@ -1603,8 +1546,8 @@ efftype = EffectType::NoEffect; if (eventFlag & 0x0020) { std::string id = ctr.readString(pcsr, 2); - step.setEffectID(1, id); - efftype = Effect::toEffectType(sndsrc, id); + step.setEffectId(1, id); + efftype = effect_utils::validateEffectId(sndsrc, id); pcsr += 2; } if (eventFlag & 0x0040) { @@ -1616,8 +1559,8 @@ efftype = EffectType::NoEffect; if (eventFlag & 0x0080) { std::string id = ctr.readString(pcsr, 2); - step.setEffectID(2, id); - efftype = Effect::toEffectType(sndsrc, id); + step.setEffectId(2, id); + efftype = effect_utils::validateEffectId(sndsrc, id); pcsr += 2; } if (eventFlag & 0x0100) { @@ -1629,8 +1572,8 @@ efftype = EffectType::NoEffect; if (eventFlag & 0x0200) { std::string id = ctr.readString(pcsr, 2); - step.setEffectID(3, id); - efftype = Effect::toEffectType(sndsrc, id); + step.setEffectId(3, id); + efftype = effect_utils::validateEffectId(sndsrc, id); pcsr += 2; } if (eventFlag & 0x0400) { @@ -1664,6 +1607,41 @@ return globCsr + songOfs; } +} + +BtmIO::BtmIO() : AbstractModuleIO("btm", "BambooTracker module", true, true) {} + +void BtmIO::load(const BinaryContainer& ctr, std::weak_ptr mod, + std::weak_ptr instMan) const +{ + size_t globCsr = 0; + if (ctr.readString(globCsr, 16) != "BambooTrackerMod") + throw FileCorruptionError(FileType::Mod, globCsr); + globCsr += 16; + size_t eofOfs = ctr.readUint32(globCsr); + size_t eof = globCsr + eofOfs; + globCsr += 4; + size_t fileVersion = ctr.readUint32(globCsr); + if (fileVersion > Version::ofModuleFileInBCD()) + throw FileVersionError(FileType::Mod); + globCsr += 4; + + while (globCsr < eof) { + if (ctr.readString(globCsr, 8) == "MODULE ") + globCsr = loadModuleSection(mod, ctr, globCsr + 8, fileVersion); + else if (ctr.readString(globCsr, 8) == "INSTRMNT") + globCsr = loadInstrumentSection(instMan, ctr, globCsr + 8, fileVersion); + else if (ctr.readString(globCsr, 8) == "INSTPROP") + globCsr = loadInstrumentPropertySection(instMan, ctr, globCsr + 8, fileVersion); + else if (ctr.readString(globCsr, 8) == "GROOVE ") + globCsr = loadGrooveSection(mod, ctr, globCsr + 8, fileVersion); + else if (ctr.readString(globCsr, 8) == "SONG ") + globCsr = loadSongSection(mod, ctr, globCsr + 8, fileVersion); + else + throw FileCorruptionError(FileType::Mod, globCsr); + } +} + void BtmIO::save(BinaryContainer& ctr, const std::weak_ptr mod, const std::weak_ptr instMan) const { @@ -1708,7 +1686,7 @@ ctr.appendString("INSTRMNT"); size_t instOfs = ctr.size(); ctr.appendUint32(0); // Dummy instrument section offset - std::vector instIdcs = instManLocked->getEntriedInstrumentIndices(); + std::vector instIdcs = instManLocked->getInstrumentIndices(); ctr.appendUint8(static_cast(instIdcs.size())); for (auto& idx : instIdcs) { if (std::shared_ptr inst = instManLocked->getInstrumentSharedPtr(idx)) { @@ -1878,32 +1856,32 @@ ctr.appendUint16(0); // Dummy offset auto seq = instManLocked->getOperatorSequenceFMSequence(FM_OPSEQ_PARAMS[i], idx); ctr.appendUint16(static_cast(seq.size())); - for (auto& com : seq) { - ctr.appendUint16(static_cast(com.type)); + for (auto& unit : seq) { + ctr.appendUint16(static_cast(unit.data)); } - auto loop = instManLocked->getOperatorSequenceFMLoops(FM_OPSEQ_PARAMS[i], idx); - ctr.appendUint16(static_cast(loop.size())); - for (auto& l : loop) { - ctr.appendUint16(static_cast(l.begin)); - ctr.appendUint16(static_cast(l.end)); - ctr.appendUint8(static_cast(l.times)); + auto loops = instManLocked->getOperatorSequenceFMLoopRoot(FM_OPSEQ_PARAMS[i], idx).getAllLoops(); + ctr.appendUint16(static_cast(loops.size())); + for (auto& loop : loops) { + ctr.appendUint16(static_cast(loop.getBeginPos())); + ctr.appendUint16(static_cast(loop.getEndPos())); + ctr.appendUint8(static_cast(loop.getTimes())); } auto release = instManLocked->getOperatorSequenceFMRelease(FM_OPSEQ_PARAMS[i], idx); - switch (release.type) { - case ReleaseType::NoRelease: + switch (release.getType()) { + case InstrumentSequenceRelease::NoRelease: ctr.appendUint8(0x00); break; - case ReleaseType::FixedRelease: + case InstrumentSequenceRelease::FixedRelease: ctr.appendUint8(0x01); - ctr.appendUint16(static_cast(release.begin)); + ctr.appendUint16(static_cast(release.getBeginPos())); break; - case ReleaseType::AbsoluteRelease: + case InstrumentSequenceRelease::AbsoluteRelease: ctr.appendUint8(0x02); - ctr.appendUint16(static_cast(release.begin)); + ctr.appendUint16(static_cast(release.getBeginPos())); break; - case ReleaseType::RelativeRelease: + case InstrumentSequenceRelease::RelativeRelease: ctr.appendUint8(0x03); - ctr.appendUint16(static_cast(release.begin)); + ctr.appendUint16(static_cast(release.getBeginPos())); break; } ctr.appendUint8(0); // Skip sequence type @@ -1923,39 +1901,39 @@ ctr.appendUint16(0); // Dummy offset auto seq = instManLocked->getArpeggioFMSequence(idx); ctr.appendUint16(static_cast(seq.size())); - for (auto& com : seq) { - ctr.appendUint16(static_cast(com.type)); + for (auto& unit : seq) { + ctr.appendUint16(static_cast(unit.data)); } - auto loop = instManLocked->getArpeggioFMLoops(idx); - ctr.appendUint16(static_cast(loop.size())); - for (auto& l : loop) { - ctr.appendUint16(static_cast(l.begin)); - ctr.appendUint16(static_cast(l.end)); - ctr.appendUint8(static_cast(l.times)); + auto loops = instManLocked->getArpeggioFMLoopRoot(idx).getAllLoops(); + ctr.appendUint16(static_cast(loops.size())); + for (auto& loop : loops) { + ctr.appendUint16(static_cast(loop.getBeginPos())); + ctr.appendUint16(static_cast(loop.getEndPos())); + ctr.appendUint8(static_cast(loop.getTimes())); } auto release = instManLocked->getArpeggioFMRelease(idx); - switch (release.type) { - case ReleaseType::NoRelease: + switch (release.getType()) { + case InstrumentSequenceRelease::NoRelease: ctr.appendUint8(0x00); // If release.type is NO_RELEASE, then release.begin == -1 so omit to save it. break; - case ReleaseType::FixedRelease: + case InstrumentSequenceRelease::FixedRelease: ctr.appendUint8(0x01); - ctr.appendUint16(static_cast(release.begin)); + ctr.appendUint16(static_cast(release.getBeginPos())); break; - case ReleaseType::AbsoluteRelease: + case InstrumentSequenceRelease::AbsoluteRelease: ctr.appendUint8(0x02); - ctr.appendUint16(static_cast(release.begin)); + ctr.appendUint16(static_cast(release.getBeginPos())); break; - case ReleaseType::RelativeRelease: + case InstrumentSequenceRelease::RelativeRelease: ctr.appendUint8(0x03); - ctr.appendUint16(static_cast(release.begin)); + ctr.appendUint16(static_cast(release.getBeginPos())); break; } switch (instManLocked->getArpeggioFMType(idx)) { - case SequenceType::ABSOLUTE_SEQUENCE: ctr.appendUint8(0x00); break; - case SequenceType::FIXED_SEQUENCE: ctr.appendUint8(0x01); break; - case SequenceType::RELATIVE_SEQUENCE: ctr.appendUint8(0x02); break; + case SequenceType::AbsoluteSequence: ctr.appendUint8(0x00); break; + case SequenceType::FixedSequence: ctr.appendUint8(0x01); break; + case SequenceType::RelativeSequence: ctr.appendUint8(0x02); break; default: break; } ctr.writeUint16(ofs, static_cast(ctr.size() - ofs)); @@ -1973,38 +1951,38 @@ ctr.appendUint16(0); // Dummy offset auto seq = instManLocked->getPitchFMSequence(idx); ctr.appendUint16(static_cast(seq.size())); - for (auto& com : seq) { - ctr.appendUint16(static_cast(com.type)); + for (auto& unit : seq) { + ctr.appendUint16(static_cast(unit.data)); } - auto loop = instManLocked->getPitchFMLoops(idx); - ctr.appendUint16(static_cast(loop.size())); - for (auto& l : loop) { - ctr.appendUint16(static_cast(l.begin)); - ctr.appendUint16(static_cast(l.end)); - ctr.appendUint8(static_cast(l.times)); + auto loops = instManLocked->getPitchFMLoopRoot(idx).getAllLoops(); + ctr.appendUint16(static_cast(loops.size())); + for (auto& loop : loops) { + ctr.appendUint16(static_cast(loop.getBeginPos())); + ctr.appendUint16(static_cast(loop.getEndPos())); + ctr.appendUint8(static_cast(loop.getTimes())); } auto release = instManLocked->getPitchFMRelease(idx); - switch (release.type) { - case ReleaseType::NoRelease: + switch (release.getType()) { + case InstrumentSequenceRelease::NoRelease: ctr.appendUint8(0x00); // If release.type is NO_RELEASE, then release.begin == -1 so omit to save it. break; - case ReleaseType::FixedRelease: + case InstrumentSequenceRelease::FixedRelease: ctr.appendUint8(0x01); - ctr.appendUint16(static_cast(release.begin)); + ctr.appendUint16(static_cast(release.getBeginPos())); break; - case ReleaseType::AbsoluteRelease: + case InstrumentSequenceRelease::AbsoluteRelease: ctr.appendUint8(0x02); - ctr.appendUint16(static_cast(release.begin)); + ctr.appendUint16(static_cast(release.getBeginPos())); break; - case ReleaseType::RelativeRelease: + case InstrumentSequenceRelease::RelativeRelease: ctr.appendUint8(0x03); - ctr.appendUint16(static_cast(release.begin)); + ctr.appendUint16(static_cast(release.getBeginPos())); break; } switch (instManLocked->getPitchFMType(idx)) { - case SequenceType::ABSOLUTE_SEQUENCE: ctr.appendUint8(0x00); break; - case SequenceType::RELATIVE_SEQUENCE: ctr.appendUint8(0x02); break; + case SequenceType::AbsoluteSequence: ctr.appendUint8(0x00); break; + case SequenceType::RelativeSequence: ctr.appendUint8(0x02); break; default: break; } ctr.writeUint16(ofs, static_cast(ctr.size() - ofs)); @@ -2022,34 +2000,34 @@ ctr.appendUint16(0); // Dummy offset auto seq = instManLocked->getWaveformSSGSequence(idx); ctr.appendUint16(static_cast(seq.size())); - for (auto& com : seq) { - ctr.appendUint16(static_cast(com.type)); - ctr.appendInt32(static_cast(com.data)); - } - auto loop = instManLocked->getWaveformSSGLoops(idx); - ctr.appendUint16(static_cast(loop.size())); - for (auto& l : loop) { - ctr.appendUint16(static_cast(l.begin)); - ctr.appendUint16(static_cast(l.end)); - ctr.appendUint8(static_cast(l.times)); + for (auto& unit : seq) { + ctr.appendUint16(static_cast(unit.data)); + ctr.appendInt32(static_cast(unit.subdata)); + } + auto loops = instManLocked->getWaveformSSGLoopRoot(idx).getAllLoops(); + ctr.appendUint16(static_cast(loops.size())); + for (auto& loop : loops) { + ctr.appendUint16(static_cast(loop.getBeginPos())); + ctr.appendUint16(static_cast(loop.getEndPos())); + ctr.appendUint8(static_cast(loop.getTimes())); } auto release = instManLocked->getWaveformSSGRelease(idx); - switch (release.type) { - case ReleaseType::NoRelease: + switch (release.getType()) { + case InstrumentSequenceRelease::NoRelease: ctr.appendUint8(0x00); // If release.type is NO_RELEASE, then release.begin == -1 so omit to save it. break; - case ReleaseType::FixedRelease: + case InstrumentSequenceRelease::FixedRelease: ctr.appendUint8(0x01); - ctr.appendUint16(static_cast(release.begin)); + ctr.appendUint16(static_cast(release.getBeginPos())); break; - case ReleaseType::AbsoluteRelease: + case InstrumentSequenceRelease::AbsoluteRelease: ctr.appendUint8(0x02); - ctr.appendUint16(static_cast(release.begin)); + ctr.appendUint16(static_cast(release.getBeginPos())); break; - case ReleaseType::RelativeRelease: + case InstrumentSequenceRelease::RelativeRelease: ctr.appendUint8(0x03); - ctr.appendUint16(static_cast(release.begin)); + ctr.appendUint16(static_cast(release.getBeginPos())); break; } ctr.appendUint8(0); // Skip sequence type @@ -2068,33 +2046,33 @@ ctr.appendUint16(0); // Dummy offset auto seq = instManLocked->getToneNoiseSSGSequence(idx); ctr.appendUint16(static_cast(seq.size())); - for (auto& com : seq) { - ctr.appendUint16(static_cast(com.type)); + for (auto& unit : seq) { + ctr.appendUint16(static_cast(unit.data)); } - auto loop = instManLocked->getToneNoiseSSGLoops(idx); - ctr.appendUint16(static_cast(loop.size())); - for (auto& l : loop) { - ctr.appendUint16(static_cast(l.begin)); - ctr.appendUint16(static_cast(l.end)); - ctr.appendUint8(static_cast(l.times)); + auto loops = instManLocked->getToneNoiseSSGLoopRoot(idx).getAllLoops(); + ctr.appendUint16(static_cast(loops.size())); + for (auto& loop : loops) { + ctr.appendUint16(static_cast(loop.getBeginPos())); + ctr.appendUint16(static_cast(loop.getEndPos())); + ctr.appendUint8(static_cast(loop.getTimes())); } auto release = instManLocked->getToneNoiseSSGRelease(idx); - switch (release.type) { - case ReleaseType::NoRelease: + switch (release.getType()) { + case InstrumentSequenceRelease::NoRelease: ctr.appendUint8(0x00); // If release.type is NO_RELEASE, then release.begin == -1 so omit to save it. break; - case ReleaseType::FixedRelease: + case InstrumentSequenceRelease::FixedRelease: ctr.appendUint8(0x01); - ctr.appendUint16(static_cast(release.begin)); + ctr.appendUint16(static_cast(release.getBeginPos())); break; - case ReleaseType::AbsoluteRelease: + case InstrumentSequenceRelease::AbsoluteRelease: ctr.appendUint8(0x02); - ctr.appendUint16(static_cast(release.begin)); + ctr.appendUint16(static_cast(release.getBeginPos())); break; - case ReleaseType::RelativeRelease: + case InstrumentSequenceRelease::RelativeRelease: ctr.appendUint8(0x03); - ctr.appendUint16(static_cast(release.begin)); + ctr.appendUint16(static_cast(release.getBeginPos())); break; } ctr.appendUint8(0); // Skip sequence type @@ -2113,35 +2091,35 @@ ctr.appendUint16(0); // Dummy offset auto seq = instManLocked->getEnvelopeSSGSequence(idx); ctr.appendUint16(static_cast(seq.size())); - for (auto& com : seq) { - ctr.appendUint16(static_cast(com.type)); - ctr.appendInt32(static_cast(com.data)); - } - auto loop = instManLocked->getEnvelopeSSGLoops(idx); - ctr.appendUint16(static_cast(loop.size())); - for (auto& l : loop) { - ctr.appendUint16(static_cast(l.begin)); - ctr.appendUint16(static_cast(l.end)); - ctr.appendUint8(static_cast(l.times)); + for (auto& unit : seq) { + ctr.appendUint16(static_cast(unit.data)); + ctr.appendInt32(static_cast(unit.subdata)); + } + auto loops = instManLocked->getEnvelopeSSGLoopRoot(idx).getAllLoops(); + ctr.appendUint16(static_cast(loops.size())); + for (auto& loop : loops) { + ctr.appendUint16(static_cast(loop.getBeginPos())); + ctr.appendUint16(static_cast(loop.getEndPos())); + ctr.appendUint8(static_cast(loop.getTimes())); } auto release = instManLocked->getEnvelopeSSGRelease(idx); - switch (release.type) { - case ReleaseType::NoRelease: + switch (release.getType()) { + case InstrumentSequenceRelease::NoRelease: ctr.appendUint8(0x00); // If release.type is NO_RELEASE, then release.begin == -1 so omit to save it. break; - case ReleaseType::FixedRelease: + case InstrumentSequenceRelease::FixedRelease: ctr.appendUint8(0x01); - ctr.appendUint16(static_cast(release.begin)); + ctr.appendUint16(static_cast(release.getBeginPos())); break; - case ReleaseType::AbsoluteRelease: + case InstrumentSequenceRelease::AbsoluteRelease: ctr.appendUint8(0x02); - ctr.appendUint16(static_cast(release.begin)); + ctr.appendUint16(static_cast(release.getBeginPos())); break; - case ReleaseType::RelativeRelease: + case InstrumentSequenceRelease::RelativeRelease: ctr.appendUint8(0x03); - ctr.appendUint16(static_cast(release.begin)); + ctr.appendUint16(static_cast(release.getBeginPos())); break; } ctr.appendUint8(0); // Skip sequence type @@ -2160,39 +2138,39 @@ ctr.appendUint16(0); // Dummy offset auto seq = instManLocked->getArpeggioSSGSequence(idx); ctr.appendUint16(static_cast(seq.size())); - for (auto& com : seq) { - ctr.appendUint16(static_cast(com.type)); + for (auto& unit : seq) { + ctr.appendUint16(static_cast(unit.data)); } - auto loop = instManLocked->getArpeggioSSGLoops(idx); - ctr.appendUint16(static_cast(loop.size())); - for (auto& l : loop) { - ctr.appendUint16(static_cast(l.begin)); - ctr.appendUint16(static_cast(l.end)); - ctr.appendUint8(static_cast(l.times)); + auto loops = instManLocked->getArpeggioSSGLoopRoot(idx).getAllLoops(); + ctr.appendUint16(static_cast(loops.size())); + for (auto& loop : loops) { + ctr.appendUint16(static_cast(loop.getBeginPos())); + ctr.appendUint16(static_cast(loop.getEndPos())); + ctr.appendUint8(static_cast(loop.getTimes())); } auto release = instManLocked->getArpeggioSSGRelease(idx); - switch (release.type) { - case ReleaseType::NoRelease: + switch (release.getType()) { + case InstrumentSequenceRelease::NoRelease: ctr.appendUint8(0x00); // If release.type is NO_RELEASE, then release.begin == -1 so omit to save it. break; - case ReleaseType::FixedRelease: + case InstrumentSequenceRelease::FixedRelease: ctr.appendUint8(0x01); - ctr.appendUint16(static_cast(release.begin)); + ctr.appendUint16(static_cast(release.getBeginPos())); break; - case ReleaseType::AbsoluteRelease: + case InstrumentSequenceRelease::AbsoluteRelease: ctr.appendUint8(0x02); - ctr.appendUint16(static_cast(release.begin)); + ctr.appendUint16(static_cast(release.getBeginPos())); break; - case ReleaseType::RelativeRelease: + case InstrumentSequenceRelease::RelativeRelease: ctr.appendUint8(0x03); - ctr.appendUint16(static_cast(release.begin)); + ctr.appendUint16(static_cast(release.getBeginPos())); break; } switch (instManLocked->getArpeggioSSGType(idx)) { - case SequenceType::ABSOLUTE_SEQUENCE: ctr.appendUint8(0x00); break; - case SequenceType::FIXED_SEQUENCE: ctr.appendUint8(0x01); break; - case SequenceType::RELATIVE_SEQUENCE: ctr.appendUint8(0x02); break; + case SequenceType::AbsoluteSequence: ctr.appendUint8(0x00); break; + case SequenceType::FixedSequence: ctr.appendUint8(0x01); break; + case SequenceType::RelativeSequence: ctr.appendUint8(0x02); break; default: break; } ctr.writeUint16(ofs, static_cast(ctr.size() - ofs)); @@ -2210,38 +2188,38 @@ ctr.appendUint16(0); // Dummy offset auto seq = instManLocked->getPitchSSGSequence(idx); ctr.appendUint16(static_cast(seq.size())); - for (auto& com : seq) { - ctr.appendUint16(static_cast(com.type)); + for (auto& unit : seq) { + ctr.appendUint16(static_cast(unit.data)); } - auto loop = instManLocked->getPitchSSGLoops(idx); - ctr.appendUint16(static_cast(loop.size())); - for (auto& l : loop) { - ctr.appendUint16(static_cast(l.begin)); - ctr.appendUint16(static_cast(l.end)); - ctr.appendUint8(static_cast(l.times)); + auto loops = instManLocked->getPitchSSGLoopRoot(idx).getAllLoops(); + ctr.appendUint16(static_cast(loops.size())); + for (auto& loop : loops) { + ctr.appendUint16(static_cast(loop.getBeginPos())); + ctr.appendUint16(static_cast(loop.getEndPos())); + ctr.appendUint8(static_cast(loop.getTimes())); } auto release = instManLocked->getPitchSSGRelease(idx); - switch (release.type) { - case ReleaseType::NoRelease: + switch (release.getType()) { + case InstrumentSequenceRelease::NoRelease: ctr.appendUint8(0x00); // If release.type is NO_RELEASE, then release.begin == -1 so omit to save it. break; - case ReleaseType::FixedRelease: + case InstrumentSequenceRelease::FixedRelease: ctr.appendUint8(0x01); - ctr.appendUint16(static_cast(release.begin)); + ctr.appendUint16(static_cast(release.getBeginPos())); break; - case ReleaseType::AbsoluteRelease: + case InstrumentSequenceRelease::AbsoluteRelease: ctr.appendUint8(0x02); - ctr.appendUint16(static_cast(release.begin)); + ctr.appendUint16(static_cast(release.getBeginPos())); break; - case ReleaseType::RelativeRelease: + case InstrumentSequenceRelease::RelativeRelease: ctr.appendUint8(0x03); - ctr.appendUint16(static_cast(release.begin)); + ctr.appendUint16(static_cast(release.getBeginPos())); break; } switch (instManLocked->getPitchSSGType(idx)) { - case SequenceType::ABSOLUTE_SEQUENCE: ctr.appendUint8(0x00); break; - case SequenceType::RELATIVE_SEQUENCE: ctr.appendUint8(0x02); break; + case SequenceType::AbsoluteSequence: ctr.appendUint8(0x00); break; + case SequenceType::RelativeSequence: ctr.appendUint8(0x02); break; default: break; } ctr.writeUint16(ofs, static_cast(ctr.size() - ofs)); @@ -2278,35 +2256,35 @@ ctr.appendUint16(0); // Dummy offset auto seq = instManLocked->getEnvelopeADPCMSequence(idx); ctr.appendUint16(static_cast(seq.size())); - for (auto& com : seq) { - ctr.appendUint16(static_cast(com.type)); - ctr.appendInt32(static_cast(com.data)); - } - auto loop = instManLocked->getEnvelopeADPCMLoops(idx); - ctr.appendUint16(static_cast(loop.size())); - for (auto& l : loop) { - ctr.appendUint16(static_cast(l.begin)); - ctr.appendUint16(static_cast(l.end)); - ctr.appendUint8(static_cast(l.times)); + for (auto& unit : seq) { + ctr.appendUint16(static_cast(unit.data)); + ctr.appendInt32(0); // Dummy set for past format + } + auto loops = instManLocked->getEnvelopeADPCMLoopRoot(idx).getAllLoops(); + ctr.appendUint16(static_cast(loops.size())); + for (auto& loop : loops) { + ctr.appendUint16(static_cast(loop.getBeginPos())); + ctr.appendUint16(static_cast(loop.getEndPos())); + ctr.appendUint8(static_cast(loop.getTimes())); } auto release = instManLocked->getEnvelopeADPCMRelease(idx); - switch (release.type) { - case ReleaseType::NoRelease: + switch (release.getType()) { + case InstrumentSequenceRelease::NoRelease: ctr.appendUint8(0x00); // If release.type is NO_RELEASE, then release.begin == -1 so omit to save it. break; - case ReleaseType::FixedRelease: + case InstrumentSequenceRelease::FixedRelease: ctr.appendUint8(0x01); - ctr.appendUint16(static_cast(release.begin)); + ctr.appendUint16(static_cast(release.getBeginPos())); break; - case ReleaseType::AbsoluteRelease: + case InstrumentSequenceRelease::AbsoluteRelease: ctr.appendUint8(0x02); - ctr.appendUint16(static_cast(release.begin)); + ctr.appendUint16(static_cast(release.getBeginPos())); break; - case ReleaseType::RelativeRelease: + case InstrumentSequenceRelease::RelativeRelease: ctr.appendUint8(0x03); - ctr.appendUint16(static_cast(release.begin)); + ctr.appendUint16(static_cast(release.getBeginPos())); break; } ctr.appendUint8(0); // Skip sequence type @@ -2325,39 +2303,39 @@ ctr.appendUint16(0); // Dummy offset auto seq = instManLocked->getArpeggioADPCMSequence(idx); ctr.appendUint16(static_cast(seq.size())); - for (auto& com : seq) { - ctr.appendUint16(static_cast(com.type)); + for (auto& unit : seq) { + ctr.appendUint16(static_cast(unit.data)); } - auto loop = instManLocked->getArpeggioADPCMLoops(idx); - ctr.appendUint16(static_cast(loop.size())); - for (auto& l : loop) { - ctr.appendUint16(static_cast(l.begin)); - ctr.appendUint16(static_cast(l.end)); - ctr.appendUint8(static_cast(l.times)); + auto loops = instManLocked->getArpeggioADPCMLoopRoot(idx).getAllLoops(); + ctr.appendUint16(static_cast(loops.size())); + for (auto& loop : loops) { + ctr.appendUint16(static_cast(loop.getBeginPos())); + ctr.appendUint16(static_cast(loop.getEndPos())); + ctr.appendUint8(static_cast(loop.getTimes())); } auto release = instManLocked->getArpeggioADPCMRelease(idx); - switch (release.type) { - case ReleaseType::NoRelease: + switch (release.getType()) { + case InstrumentSequenceRelease::NoRelease: ctr.appendUint8(0x00); // If release.type is NO_RELEASE, then release.begin == -1 so omit to save it. break; - case ReleaseType::FixedRelease: + case InstrumentSequenceRelease::FixedRelease: ctr.appendUint8(0x01); - ctr.appendUint16(static_cast(release.begin)); + ctr.appendUint16(static_cast(release.getBeginPos())); break; - case ReleaseType::AbsoluteRelease: + case InstrumentSequenceRelease::AbsoluteRelease: ctr.appendUint8(0x02); - ctr.appendUint16(static_cast(release.begin)); + ctr.appendUint16(static_cast(release.getBeginPos())); break; - case ReleaseType::RelativeRelease: + case InstrumentSequenceRelease::RelativeRelease: ctr.appendUint8(0x03); - ctr.appendUint16(static_cast(release.begin)); + ctr.appendUint16(static_cast(release.getBeginPos())); break; } switch (instManLocked->getArpeggioADPCMType(idx)) { - case SequenceType::ABSOLUTE_SEQUENCE: ctr.appendUint8(0x00); break; - case SequenceType::FIXED_SEQUENCE: ctr.appendUint8(0x01); break; - case SequenceType::RELATIVE_SEQUENCE: ctr.appendUint8(0x02); break; + case SequenceType::AbsoluteSequence: ctr.appendUint8(0x00); break; + case SequenceType::FixedSequence: ctr.appendUint8(0x01); break; + case SequenceType::RelativeSequence: ctr.appendUint8(0x02); break; default: break; } ctr.writeUint16(ofs, static_cast(ctr.size() - ofs)); @@ -2375,38 +2353,38 @@ ctr.appendUint16(0); // Dummy offset auto seq = instManLocked->getPitchADPCMSequence(idx); ctr.appendUint16(static_cast(seq.size())); - for (auto& com : seq) { - ctr.appendUint16(static_cast(com.type)); + for (auto& unit : seq) { + ctr.appendUint16(static_cast(unit.data)); } - auto loop = instManLocked->getPitchADPCMLoops(idx); - ctr.appendUint16(static_cast(loop.size())); - for (auto& l : loop) { - ctr.appendUint16(static_cast(l.begin)); - ctr.appendUint16(static_cast(l.end)); - ctr.appendUint8(static_cast(l.times)); + auto loops = instManLocked->getPitchADPCMLoopRoot(idx).getAllLoops(); + ctr.appendUint16(static_cast(loops.size())); + for (auto& loop : loops) { + ctr.appendUint16(static_cast(loop.getBeginPos())); + ctr.appendUint16(static_cast(loop.getEndPos())); + ctr.appendUint8(static_cast(loop.getTimes())); } auto release = instManLocked->getPitchADPCMRelease(idx); - switch (release.type) { - case ReleaseType::NoRelease: + switch (release.getType()) { + case InstrumentSequenceRelease::NoRelease: ctr.appendUint8(0x00); // If release.type is NO_RELEASE, then release.begin == -1 so omit to save it. break; - case ReleaseType::FixedRelease: + case InstrumentSequenceRelease::FixedRelease: ctr.appendUint8(0x01); - ctr.appendUint16(static_cast(release.begin)); + ctr.appendUint16(static_cast(release.getBeginPos())); break; - case ReleaseType::AbsoluteRelease: + case InstrumentSequenceRelease::AbsoluteRelease: ctr.appendUint8(0x02); - ctr.appendUint16(static_cast(release.begin)); + ctr.appendUint16(static_cast(release.getBeginPos())); break; - case ReleaseType::RelativeRelease: + case InstrumentSequenceRelease::RelativeRelease: ctr.appendUint8(0x03); - ctr.appendUint16(static_cast(release.begin)); + ctr.appendUint16(static_cast(release.getBeginPos())); break; } switch (instManLocked->getPitchADPCMType(idx)) { - case SequenceType::ABSOLUTE_SEQUENCE: ctr.appendUint8(0x00); break; - case SequenceType::RELATIVE_SEQUENCE: ctr.appendUint8(0x02); break; + case SequenceType::AbsoluteSequence: ctr.appendUint8(0x00); break; + case SequenceType::RelativeSequence: ctr.appendUint8(0x02); break; default: break; } ctr.writeUint16(ofs, static_cast(ctr.size() - ofs)); @@ -2424,7 +2402,7 @@ ctr.appendUint8(static_cast(grooveCnt - 1)); for (size_t i = 0; i < grooveCnt; ++i) { ctr.appendUint8(static_cast(i)); - auto seq = mod.lock()->getGroove(static_cast(i)).getSequence(); + auto seq = mod.lock()->getGroove(static_cast(i)); ctr.appendUint8(static_cast(seq.size())); for (auto& g : seq) { ctr.appendUint8(static_cast(g)); @@ -2483,7 +2461,7 @@ size_t odrSize = track.getOrderSize(); ctr.appendUint8(static_cast(odrSize) - 1); for (size_t o = 0; o < odrSize; ++o) - ctr.appendUint8(static_cast(track.getOrderData(static_cast(o)).patten)); + ctr.appendUint8(static_cast(track.getOrderInfo(static_cast(o)).patten)); ctr.appendUint8(static_cast(track.getEffectDisplayWidth())); // Pattern @@ -2502,28 +2480,28 @@ auto& step = pattern.getStep(sidx); uint16_t eventFlag = 0; int tmp = step.getNoteNumber(); - if (tmp != -1) { + if (!Step::testEmptyNote(tmp)) { eventFlag |= 0x0001; ctr.appendInt8(static_cast(tmp)); } tmp = step.getInstrumentNumber(); - if (tmp != -1) { + if (!Step::testEmptyInstrument(tmp)) { eventFlag |= 0x0002; ctr.appendUint8(static_cast(tmp)); } tmp = step.getVolume(); - if (tmp != -1) { + if (!Step::testEmptyVolume(tmp)) { eventFlag |= 0x0004; ctr.appendUint8(static_cast(tmp)); } - for (int i = 0; i < 4; ++i) { - std::string tmpstr = step.getEffectID(i); - if (tmpstr != "--") { + for (int i = 0; i < Step::N_EFFECT; ++i) { + std::string tmpstr = step.getEffectId(i); + if (!Step::testEmptyEffectId(tmpstr)) { eventFlag |= (0x0008 << (i << 1)); ctr.appendString(tmpstr); } tmp = step.getEffectValue(i); - if (tmp != -1) { + if (!Step::testEmptyEffectValue(tmp)) { eventFlag |= (0x0010 << (i << 1)); ctr.appendUint8(static_cast(tmp)); } diff -Nru bambootracker-0.4.5+git20121209/BambooTracker/io/btm_io.hpp bambootracker-0.4.6/BambooTracker/io/btm_io.hpp --- bambootracker-0.4.5+git20121209/BambooTracker/io/btm_io.hpp 2020-12-08 14:05:28.000000000 +0000 +++ bambootracker-0.4.6/BambooTracker/io/btm_io.hpp 2021-02-11 14:37:17.000000000 +0000 @@ -37,23 +37,5 @@ std::weak_ptr instMan) const override; void save(BinaryContainer& ctr, const std::weak_ptr mod, const std::weak_ptr instMan) const override; - -private: - static size_t loadModuleSectionInModule(std::weak_ptr mod, const BinaryContainer& ctr, - size_t globCsr, uint32_t version); - static size_t loadInstrumentSectionInModule(std::weak_ptr instMan, - const BinaryContainer& ctr, size_t globCsr, - uint32_t version); - static size_t loadInstrumentPropertySectionInModule(std::weak_ptr instMan, - const BinaryContainer& ctr, size_t globCsr, - uint32_t version); - static size_t loadInstrumentPropertyOperatorSequence(FMEnvelopeParameter param, - size_t instMemCsr, - std::shared_ptr& instManLocked, - const BinaryContainer& ctr, uint32_t version); - static size_t loadGrooveSectionInModule(std::weak_ptr mod, const BinaryContainer& ctr, - size_t globCsr, uint32_t version); - static size_t loadSongSectionInModule(std::weak_ptr mod, const BinaryContainer& ctr, - size_t globCsr, uint32_t version); }; } diff -Nru bambootracker-0.4.5+git20121209/BambooTracker/io/dat_io.cpp bambootracker-0.4.6/BambooTracker/io/dat_io.cpp --- bambootracker-0.4.5+git20121209/BambooTracker/io/dat_io.cpp 2020-12-08 14:05:28.000000000 +0000 +++ bambootracker-0.4.6/BambooTracker/io/dat_io.cpp 2021-02-11 14:37:17.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (C) 2020 Rerrah + * Copyright (C) 2020-2021 Rerrah * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation @@ -26,7 +26,6 @@ #include "dat_io.hpp" #include #include -#include #include "instrument.hpp" #include "file_io_error.hpp" @@ -57,7 +56,7 @@ csr += 6; // Empty - if (std::all_of(block.getPointer(), block.getPointer() + 25, + if (std::all_of(block.begin(), block.end(), [](const char c) { return c == 0; }) && name.empty()) continue; @@ -66,7 +65,7 @@ ctrs.push_back(block); } - return new Mucom88Bank(std::move(ids), std::move(names), std::move(ctrs)); + return new Mucom88Bank(ids, names, ctrs); } AbstractInstrument* DatIO::loadInstrument(const BinaryContainer& instCtr, diff -Nru bambootracker-0.4.5+git20121209/BambooTracker/io/dmp_io.cpp bambootracker-0.4.6/BambooTracker/io/dmp_io.cpp --- bambootracker-0.4.5+git20121209/BambooTracker/io/dmp_io.cpp 2020-12-08 14:05:28.000000000 +0000 +++ bambootracker-0.4.6/BambooTracker/io/dmp_io.cpp 2021-02-11 14:37:17.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (C) 2020 Rerrah + * Copyright (C) 2020-2021 Rerrah * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation @@ -71,11 +71,11 @@ // compensate SN76489's envelope step of 2dB to SSG's 3dB if (data > 0) data = 15 - (15 - data) * 2 / 3; csr += 4; - if (l == 0) instManLocked->setEnvelopeSSGSequenceCommand(idx, 0, data, 0); - else instManLocked->addEnvelopeSSGSequenceCommand(idx, data, 0); + if (l == 0) instManLocked->setEnvelopeSSGSequenceData(idx, 0, SSGEnvelopeUnit::makeOnlyDataUnit(data)); + else instManLocked->addEnvelopeSSGSequenceData(idx, SSGEnvelopeUnit::makeOnlyDataUnit(data)); } int8_t loop = ctr.readInt8(csr++); - if (loop >= 0) instManLocked->setEnvelopeSSGLoops(idx, {loop}, {envSize - 1}, {1}); + if (loop >= 0) instManLocked->addEnvelopeSSGLoop(idx, InstrumentSequenceLoop(loop, envSize - 1)); } uint8_t arpSize = ctr.readUint8(csr++); if (arpSize > 0) { @@ -84,16 +84,16 @@ ssg->setArpeggioEnabled(true); ssg->setArpeggioNumber(idx); uint8_t arpType = ctr.readUint8(csr + arpSize * 4 + 1); - if (arpType == 1) instManLocked->setArpeggioSSGType(idx, SequenceType::FIXED_SEQUENCE); + if (arpType == 1) instManLocked->setArpeggioSSGType(idx, SequenceType::FixedSequence); for (uint8_t l = 0; l < arpSize; ++l) { int data = ctr.readInt32(csr) + 36; csr += 4; if (arpType == 1) data -= 24; - if (l == 0) instManLocked->setArpeggioSSGSequenceCommand(idx, 0, data, 0); - else instManLocked->addArpeggioSSGSequenceCommand(idx, data, 0); + if (l == 0) instManLocked->setArpeggioSSGSequenceData(idx, 0, data); + else instManLocked->addArpeggioSSGSequenceData(idx, data); } int8_t loop = ctr.readInt8(csr++); - if (loop >= 0) instManLocked->setArpeggioSSGLoops(idx, {loop}, {arpSize - 1}, {1}); + if (loop >= 0) instManLocked->addArpeggioSSGLoop(idx, InstrumentSequenceLoop(loop, arpSize - 1)); } break; } @@ -110,7 +110,7 @@ instManLocked->setEnvelopeFMParameter(envIdx, FMEnvelopeParameter::AL, ctr.readUint8(csr++)); uint8_t ams = ctr.readUint8(csr++); - uint8_t am[4]; + uint8_t am[4] = {}; for (const int op : { 0, 2, 1, 3 }) { auto& params = FM_OP_PARAMS[op]; instManLocked->setEnvelopeFMParameter(envIdx, params.at(FMOperatorParameter::ML), ctr.readUint8(csr++)); diff -Nru bambootracker-0.4.5+git20121209/BambooTracker/io/export_io.cpp bambootracker-0.4.6/BambooTracker/io/export_io.cpp --- bambootracker-0.4.5+git20121209/BambooTracker/io/export_io.cpp 2020-12-08 14:05:28.000000000 +0000 +++ bambootracker-0.4.6/BambooTracker/io/export_io.cpp 2021-02-11 14:37:17.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (C) 2019-2020 Rerrah + * Copyright (C) 2019-2021 Rerrah * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation @@ -30,9 +30,9 @@ namespace io { -void writeVgm(BinaryContainer& container, int target, std::vector samples, uint32_t clock, uint32_t rate, - bool loopFlag, uint32_t loopPoint, uint32_t loopSamples, uint32_t totalSamples, - bool gd3TagEnabled, GD3Tag tag) +void writeVgm(BinaryContainer& container, int target, const std::vector& samples, + uint32_t clock, uint32_t rate, bool loopFlag, uint32_t loopPoint, + uint32_t loopSamples, uint32_t totalSamples, bool gd3TagEnabled, const GD3Tag& tag) { uint32_t tagLen = 0; uint32_t tagDataLen = 0; @@ -143,8 +143,9 @@ } } -void writeS98(BinaryContainer& container, int target, std::vector samples, uint32_t clock, uint32_t rate, - bool loopFlag, uint32_t loopPoint, bool tagEnabled, S98Tag tag) +void writeS98(BinaryContainer& container, int target, const std::vector& samples, + uint32_t clock, uint32_t rate, bool loopFlag, uint32_t loopPoint, + bool tagEnabled, const S98Tag& tag) { // Header // 0x00: Magic "S98" diff -Nru bambootracker-0.4.5+git20121209/BambooTracker/io/export_io.hpp bambootracker-0.4.6/BambooTracker/io/export_io.hpp --- bambootracker-0.4.5+git20121209/BambooTracker/io/export_io.hpp 2020-12-08 14:05:28.000000000 +0000 +++ bambootracker-0.4.6/BambooTracker/io/export_io.hpp 2021-02-11 14:37:17.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (C) 2019-2020 Rerrah + * Copyright (C) 2019-2021 Rerrah * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation @@ -44,9 +44,9 @@ std::string notes; }; -void writeVgm(BinaryContainer& container, int target, std::vector samples, uint32_t clock, uint32_t rate, - bool loopFlag, uint32_t loopPoint, uint32_t loopSamples, uint32_t totalSamples, - bool gd3TagEnabled, GD3Tag tag); +void writeVgm(BinaryContainer& container, int target, const std::vector& samples, + uint32_t clock, uint32_t rate, bool loopFlag, uint32_t loopPoint, + uint32_t loopSamples, uint32_t totalSamples, bool gd3TagEnabled, const GD3Tag& tag); // S98 ---------- struct S98Tag @@ -62,8 +62,9 @@ std::string system; }; -void writeS98(BinaryContainer& container, int target, std::vector samples, uint32_t clock, uint32_t rate, - bool loopFlag, uint32_t loopPoint, bool tagEnabled, S98Tag tag); +void writeS98(BinaryContainer& container, int target, const std::vector& samples, + uint32_t clock, uint32_t rate, bool loopFlag, uint32_t loopPoint, + bool tagEnabled, const S98Tag& tag); enum ExportTargetFlag { diff -Nru bambootracker-0.4.5+git20121209/BambooTracker/io/ff_io.cpp bambootracker-0.4.6/BambooTracker/io/ff_io.cpp --- bambootracker-0.4.5+git20121209/BambooTracker/io/ff_io.cpp 2020-12-08 14:05:28.000000000 +0000 +++ bambootracker-0.4.6/BambooTracker/io/ff_io.cpp 2021-02-11 14:37:17.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (C) 2020 Rerrah + * Copyright (C) 2020-2021 Rerrah * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation @@ -26,7 +26,6 @@ #include "ff_io.hpp" #include #include -#include #include "instrument.hpp" #include "file_io_error.hpp" @@ -57,7 +56,7 @@ csr += 7; // Empty - if (std::all_of(block.getPointer(), block.getPointer() + 25, + if (std::all_of(block.begin(), block.end(), [](const char c) { return c == 0; }) && name.empty()) continue; @@ -66,7 +65,7 @@ ctrs.push_back(block); } - return new FfBank(std::move(ids), std::move(names), std::move(ctrs)); + return new FfBank(ids, names, ctrs); } AbstractInstrument* FfIO::loadInstrument(const BinaryContainer& instCtr, @@ -112,7 +111,7 @@ instManLocked->setEnvelopeFMParameter(envIdx, params.at(FMOperatorParameter::SL), tmp >> 4); instManLocked->setEnvelopeFMParameter(envIdx, params.at(FMOperatorParameter::RR), tmp & 0x0f); } - tmp = instCtr.readUint8(25); + tmp = instCtr.readUint8(24); instManLocked->setEnvelopeFMParameter(envIdx, FMEnvelopeParameter::FB, tmp >> 3); instManLocked->setEnvelopeFMParameter(envIdx, FMEnvelopeParameter::AL, tmp & 0x07); diff -Nru bambootracker-0.4.5+git20121209/BambooTracker/io/file_io_error.hpp bambootracker-0.4.6/BambooTracker/io/file_io_error.hpp --- bambootracker-0.4.5+git20121209/BambooTracker/io/file_io_error.hpp 2020-12-08 14:05:28.000000000 +0000 +++ bambootracker-0.4.6/BambooTracker/io/file_io_error.hpp 2021-02-11 14:37:17.000000000 +0000 @@ -37,7 +37,7 @@ protected: const FileType ftype_; - FileIOError(std::string text, const FileType ftype) + FileIOError(const std::string& text, const FileType ftype) : std::runtime_error(text), ftype_(ftype) {} public: diff -Nru bambootracker-0.4.5+git20121209/BambooTracker/io/instrument_io.hpp bambootracker-0.4.6/BambooTracker/io/instrument_io.hpp --- bambootracker-0.4.5+git20121209/BambooTracker/io/instrument_io.hpp 2020-12-08 14:05:28.000000000 +0000 +++ bambootracker-0.4.6/BambooTracker/io/instrument_io.hpp 2021-02-11 14:37:17.000000000 +0000 @@ -38,7 +38,7 @@ class AbstractInstrumentIO { public: - AbstractInstrumentIO(std::string ext, std::string desc, bool loadable, bool savable) + AbstractInstrumentIO(const std::string& ext, const std::string& desc, bool loadable, bool savable) : ext_(ext), desc_(desc), loadable_(loadable), savable_(savable) {} virtual ~AbstractInstrumentIO() = default; virtual AbstractInstrument* load(const BinaryContainer& ctr, const std::string& fileName, @@ -52,7 +52,7 @@ inline bool isSavable() const noexcept { return savable_; } private: - std::string ext_, desc_; + const std::string ext_, desc_; bool loadable_, savable_; }; diff -Nru bambootracker-0.4.5+git20121209/BambooTracker/io/io_utils.cpp bambootracker-0.4.6/BambooTracker/io/io_utils.cpp --- bambootracker-0.4.5+git20121209/BambooTracker/io/io_utils.cpp 2020-12-08 14:05:28.000000000 +0000 +++ bambootracker-0.4.6/BambooTracker/io/io_utils.cpp 2021-02-11 14:37:17.000000000 +0000 @@ -1,8 +1,33 @@ +/* + * Copyright (C) 2018-2021 Rerrah + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + #include "io_utils.hpp" #include #include "binary_container.hpp" -#include "envelope_fm.hpp" -#include "misc.hpp" +#include "instrument/envelope_fm.hpp" +#include "instrument/instrument_property_defs.hpp" namespace io { @@ -91,25 +116,4 @@ default: throw std::out_of_range("Out of range dt"); } } - -void extractADPCMSamples(const BinaryContainer& ctr, size_t addrPos, size_t sampOffs, - int maxCnt, std::vector& ids, - std::vector>& samples) -{ - size_t ofs = 0; - for (int i = 0; i < maxCnt; ++i) { - uint16_t start = ctr.readUint16(addrPos); - addrPos += 2; - uint16_t stop = ctr.readUint16(addrPos); - addrPos += 2; - - if (stop && start <= stop) { - if (ids.empty()) ofs = start; - ids.push_back(i); - size_t st = sampOffs + static_cast((start - ofs) << 5); - size_t sampSize = std::min(static_cast((stop + 1 - start) << 5), ctr.size() - st); - samples.push_back(ctr.getSubcontainer(st, sampSize).toVector()); - } - } -} } diff -Nru bambootracker-0.4.5+git20121209/BambooTracker/io/io_utils.hpp bambootracker-0.4.6/BambooTracker/io/io_utils.hpp --- bambootracker-0.4.5+git20121209/BambooTracker/io/io_utils.hpp 2020-12-08 14:05:28.000000000 +0000 +++ bambootracker-0.4.6/BambooTracker/io/io_utils.hpp 2021-02-11 14:37:17.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (C) 2018-2020 Rerrah + * Copyright (C) 2018-2021 Rerrah * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation @@ -23,7 +23,6 @@ * OTHER DEALINGS IN THE SOFTWARE. */ - #pragma once #include @@ -54,7 +53,7 @@ inline bool containExtension(const std::string& ext) const { return map_.count(ext); } inline bool testLoadableExtension(const std::string& ext) const { return (map_.count(ext) && map_.at(ext)->isLoadable()); } inline bool testSavableExtension(const std::string& ext) const { return (map_.count(ext) && map_.at(ext)->isSavable()); } - inline const std::unique_ptr& at(std::string ext) const { return map_.at(ext); } + inline const std::unique_ptr& at(const std::string& ext) const { return map_.at(ext); } inline std::vector getLoadFilterList() const noexcept { return ldFilters_; } inline std::vector getSaveFilterList() const noexcept { return svFilters_; } @@ -80,8 +79,4 @@ } int convertDtFromDmpTfiVgi(int dt); - -void extractADPCMSamples(const BinaryContainer& ctr, size_t addrPos, size_t sampOffs, - int maxCnt, std::vector& ids, - std::vector>& samples); } diff -Nru bambootracker-0.4.5+git20121209/BambooTracker/io/module_io.hpp bambootracker-0.4.6/BambooTracker/io/module_io.hpp --- bambootracker-0.4.5+git20121209/BambooTracker/io/module_io.hpp 2020-12-08 14:05:28.000000000 +0000 +++ bambootracker-0.4.6/BambooTracker/io/module_io.hpp 2021-02-11 14:37:17.000000000 +0000 @@ -38,7 +38,7 @@ class AbstractModuleIO { public: - AbstractModuleIO(std::string ext, std::string desc, bool loadable, bool savable) + AbstractModuleIO(const std::string& ext, const std::string& desc, bool loadable, bool savable) : ext_(ext), desc_(desc), loadable_(loadable), savable_(savable) {} virtual ~AbstractModuleIO() = default; virtual void load(const BinaryContainer& ctr, std::weak_ptr mod, @@ -51,7 +51,7 @@ inline bool isSavable() const noexcept { return savable_; } private: - std::string ext_, desc_; + const std::string ext_, desc_; bool loadable_, savable_; }; diff -Nru bambootracker-0.4.5+git20121209/BambooTracker/io/opni_io.cpp bambootracker-0.4.6/BambooTracker/io/opni_io.cpp --- bambootracker-0.4.5+git20121209/BambooTracker/io/opni_io.cpp 2020-12-08 14:05:28.000000000 +0000 +++ bambootracker-0.4.6/BambooTracker/io/opni_io.cpp 2021-02-11 14:37:17.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (C) 2020 Rerrah + * Copyright (C) 2020-2021 Rerrah * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation @@ -26,6 +26,7 @@ #include "opni_io.hpp" #include "format/wopn_file.h" #include "file_io_error.hpp" +#include "note.hpp" namespace io { @@ -36,7 +37,8 @@ { (void)fileName; OPNIFile opni; - if (WOPN_LoadInstFromMem(&opni, const_cast(ctr.getPointer()), static_cast(ctr.size())) != 0) + auto&& mem = ctr.toVector(); + if (WOPN_LoadInstFromMem(&opni, mem.data(), mem.size()) != 0) throw FileCorruptionError(FileType::Inst, 0); return loadWOPNInstrument(opni.inst, instMan, instNum); @@ -99,8 +101,8 @@ if (arpIdx < 0) throw FileCorruptionError(FileType::Bank, 0); inst->setArpeggioEnabled(FMOperatorType::All, true); inst->setArpeggioNumber(FMOperatorType::All, arpIdx); - instManLocked->setArpeggioFMSequenceCommand(arpIdx, 0, srcInst.note_offset + 48, -1); - instManLocked->setArpeggioFMType(arpIdx, SequenceType::ABSOLUTE_SEQUENCE); + instManLocked->setArpeggioFMSequenceData(arpIdx, 0, srcInst.note_offset + Note::DEFAULT_NOTE_NUM); + instManLocked->setArpeggioFMType(arpIdx, SequenceType::AbsoluteSequence); } return inst; diff -Nru bambootracker-0.4.5+git20121209/BambooTracker/io/opni_io.hpp bambootracker-0.4.6/BambooTracker/io/opni_io.hpp --- bambootracker-0.4.5+git20121209/BambooTracker/io/opni_io.hpp 2020-12-08 14:05:28.000000000 +0000 +++ bambootracker-0.4.6/BambooTracker/io/opni_io.hpp 2021-02-11 14:37:17.000000000 +0000 @@ -37,6 +37,7 @@ OpniIO(); AbstractInstrument* load(const BinaryContainer& ctr, const std::string& fileName, std::weak_ptr instMan, int instNum) const override; + static AbstractInstrument* loadWOPNInstrument(const WOPNInstrument &srcInst, std::weak_ptr instMan, int instNum); diff -Nru bambootracker-0.4.5+git20121209/BambooTracker/io/p86_io.cpp bambootracker-0.4.6/BambooTracker/io/p86_io.cpp --- bambootracker-0.4.5+git20121209/BambooTracker/io/p86_io.cpp 1970-01-01 00:00:00.000000000 +0000 +++ bambootracker-0.4.6/BambooTracker/io/p86_io.cpp 2021-02-11 14:37:17.000000000 +0000 @@ -0,0 +1,106 @@ +/* + * Copyright (C) 2021 Rerrah + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +#include "p86_io.hpp" +#include +#include "instrument.hpp" +#include "file_io_error.hpp" +#include "chip/codec/ymb_codec.hpp" + +namespace io +{ +namespace +{ +inline uint32_t readUint24(const BinaryContainer& ctr, size_t offset) +{ + return ctr.readUint8(offset) | (ctr.readUint8(offset + 1) << 8u) | (ctr.readUint8(offset + 2) << 16u); +} +} + +P86IO::P86IO() : AbstractBankIO("p86", "PMD P86", true, false) {} + +AbstractBank* P86IO::load(const BinaryContainer& ctr) const +{ + using namespace std::literals::string_literals; + + std::string id = ctr.readString(0, 12); + if (id != "PCM86 DATA\n\0"s) + throw FileCorruptionError(FileType::Bank, 0); + uint32_t size = readUint24(ctr, 13); + if (size != ctr.size()) + throw FileCorruptionError(FileType::Bank, 13); + + constexpr size_t SAMP_OFFS = 0x610; + if (ctr.size() < SAMP_OFFS) throw FileCorruptionError(FileType::Bank, 0x10); + + std::vector ids; + std::vector> samples; + size_t globCsr = 0x10; + size_t offs = 0; + constexpr int MAX_CNT = 256; + for (int i = 0; i < MAX_CNT; ++i) { + uint32_t start = readUint24(ctr, globCsr); + globCsr += 3; + uint32_t len = readUint24(ctr, globCsr); + globCsr += 3; + + if (len) { + if (ids.empty()) offs = start; + ids.push_back(i); + + std::vector&& smp = ctr.getSubcontainer(SAMP_OFFS + start - offs, len).toVector(); + std::vector buf(smp.size()); + std::transform(smp.begin(), smp.end(), buf.begin(), [](uint8_t v) { + return static_cast(static_cast(v)) << 8; + }); + smp.resize((smp.size() + 1) / 2); + smp.shrink_to_fit(); + smp.back() = 0; // Clear last data + codec::ymb_encode(buf.data(), smp.data(), buf.size()); + samples.push_back(std::move(smp)); + } + } + + return new P86Bank(ids, samples); +} + +AbstractInstrument* P86IO::loadInstrument(const std::vector& sample, + std::weak_ptr instMan, + int instNum) +{ + std::shared_ptr instManLocked = instMan.lock(); + int sampIdx = instManLocked->findFirstAssignableSampleADPCM(); + if (sampIdx < 0) throw FileCorruptionError(FileType::Bank, 0); + + InstrumentADPCM* adpcm = new InstrumentADPCM(instNum, "", instManLocked.get()); + adpcm->setSampleNumber(sampIdx); + + instManLocked->storeSampleADPCMRawSample(sampIdx, sample); + instManLocked->setSampleADPCMRootKeyNumber(sampIdx, 67); // o5g + instManLocked->setSampleADPCMRootDeltaN(sampIdx, 0x4a0d); // 16540Hz + + return adpcm; +} +} diff -Nru bambootracker-0.4.5+git20121209/BambooTracker/io/p86_io.hpp bambootracker-0.4.6/BambooTracker/io/p86_io.hpp --- bambootracker-0.4.5+git20121209/BambooTracker/io/p86_io.hpp 1970-01-01 00:00:00.000000000 +0000 +++ bambootracker-0.4.6/BambooTracker/io/p86_io.hpp 2021-02-11 14:37:17.000000000 +0000 @@ -0,0 +1,42 @@ +/* + * Copyright (C) 2021 Rerrah + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +#pragma once + +#include "bank_io.hpp" + +namespace io +{ +class P86IO final : public AbstractBankIO +{ +public: + P86IO(); + AbstractBank* load(const BinaryContainer& ctr) const override; + + static AbstractInstrument* loadInstrument(const std::vector& sample, + std::weak_ptr instMan, + int instNum); +}; +} diff -Nru bambootracker-0.4.5+git20121209/BambooTracker/io/ppc_io.cpp bambootracker-0.4.6/BambooTracker/io/ppc_io.cpp --- bambootracker-0.4.5+git20121209/BambooTracker/io/ppc_io.cpp 2020-12-08 14:05:28.000000000 +0000 +++ bambootracker-0.4.6/BambooTracker/io/ppc_io.cpp 2021-02-11 14:37:17.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (C) 2020 Rerrah + * Copyright (C) 2020-2021 Rerrah * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation @@ -25,11 +25,8 @@ #include "ppc_io.hpp" #include -#include #include "instrument.hpp" #include "file_io_error.hpp" -#include "io_utils.hpp" -#include "misc.hpp" namespace io { @@ -51,12 +48,27 @@ std::vector ids; std::vector> samples; - extractADPCMSamples(ctr, globCsr, sampOffs, 256, ids, samples); + size_t offs = 0; + constexpr int MAX_CNT = 256; + for (int i = 0; i < MAX_CNT; ++i) { + uint16_t start = ctr.readUint16(globCsr); + globCsr += 2; + uint16_t stop = ctr.readUint16(globCsr); + globCsr += 2; + + if (start < stop) { + if (ids.empty()) offs = start; + ids.push_back(i); + size_t st = sampOffs + static_cast((start - offs) << 5); + size_t sampSize = std::min((stop + 1u - start) << 5, ctr.size() - st); + samples.push_back(ctr.getSubcontainer(st, sampSize).toVector()); + } + } - return new PpcBank(std::move(ids), std::move(samples)); + return new PpcBank(ids, samples); } -AbstractInstrument* PpcIO::loadInstrument(const std::vector sample, +AbstractInstrument* PpcIO::loadInstrument(const std::vector& sample, std::weak_ptr instMan, int instNum) { @@ -69,7 +81,7 @@ instManLocked->storeSampleADPCMRawSample(sampIdx, sample); instManLocked->setSampleADPCMRootKeyNumber(sampIdx, 67); // o5g - instManLocked->setSampleADPCMRootDeltaN(sampIdx, calcADPCMDeltaN(16000)); + instManLocked->setSampleADPCMRootDeltaN(sampIdx, 0x49cd); // 16000Hz return adpcm; } diff -Nru bambootracker-0.4.5+git20121209/BambooTracker/io/ppc_io.hpp bambootracker-0.4.6/BambooTracker/io/ppc_io.hpp --- bambootracker-0.4.5+git20121209/BambooTracker/io/ppc_io.hpp 2020-12-08 14:05:28.000000000 +0000 +++ bambootracker-0.4.6/BambooTracker/io/ppc_io.hpp 2021-02-11 14:37:17.000000000 +0000 @@ -35,7 +35,7 @@ PpcIO(); AbstractBank* load(const BinaryContainer& ctr) const override; - static AbstractInstrument* loadInstrument(const std::vector sample, + static AbstractInstrument* loadInstrument(const std::vector& sample, std::weak_ptr instMan, int instNum); }; diff -Nru bambootracker-0.4.5+git20121209/BambooTracker/io/pps_io.cpp bambootracker-0.4.6/BambooTracker/io/pps_io.cpp --- bambootracker-0.4.5+git20121209/BambooTracker/io/pps_io.cpp 1970-01-01 00:00:00.000000000 +0000 +++ bambootracker-0.4.6/BambooTracker/io/pps_io.cpp 2021-02-11 14:37:17.000000000 +0000 @@ -0,0 +1,86 @@ +/* + * Copyright (C) 2021 Rerrah + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +#include "pps_io.hpp" +#include +#include "instrument.hpp" +#include "file_io_error.hpp" +#include "chip/codec/ymb_codec.hpp" + +namespace io +{ +PpsIO::PpsIO() : AbstractBankIO("pps", "PMD PPS", true, false) {} + +AbstractBank* PpsIO::load(const BinaryContainer& ctr) const +{ + constexpr size_t SAMP_OFFS = 0x54; + if (ctr.size() < SAMP_OFFS) throw FileCorruptionError(FileType::Bank, 0); + + std::vector ids; + std::vector> samples; + size_t globCsr = 0; + constexpr int MAX_CNT = 14; + for (int i = 0; i < MAX_CNT; ++i) { + uint16_t start = ctr.readUint16(globCsr); + globCsr += 2; + uint16_t len = ctr.readUint16(globCsr); + globCsr += 4; // Skip 2 bytes + + if (len) { + ids.push_back(i); + + std::vector&& smp = ctr.getSubcontainer(start, len).toVector(); + std::vector buf(smp.size() * 2); + for (size_t i = 0; i < smp.size(); ++i) { + uint8_t sample = smp[i]; + buf[i] = (static_cast(sample >> 4) - 8) << 12; + buf[i + 1] = (static_cast(sample & 0x0f) - 8) << 12; + } + codec::ymb_encode(buf.data(), smp.data(), buf.size()); + samples.push_back(std::move(smp)); + } + } + + return new PpsBank(ids, samples); +} + +AbstractInstrument* PpsIO::loadInstrument(const std::vector& sample, + std::weak_ptr instMan, + int instNum) +{ + std::shared_ptr instManLocked = instMan.lock(); + int sampIdx = instManLocked->findFirstAssignableSampleADPCM(); + if (sampIdx < 0) throw FileCorruptionError(FileType::Bank, 0); + + InstrumentADPCM* adpcm = new InstrumentADPCM(instNum, "", instManLocked.get()); + adpcm->setSampleNumber(sampIdx); + + instManLocked->storeSampleADPCMRawSample(sampIdx, sample); + instManLocked->setSampleADPCMRootKeyNumber(sampIdx, 67); // o5g + instManLocked->setSampleADPCMRootDeltaN(sampIdx, 0x49cd); // 16000Hz + + return adpcm; +} +} diff -Nru bambootracker-0.4.5+git20121209/BambooTracker/io/pps_io.hpp bambootracker-0.4.6/BambooTracker/io/pps_io.hpp --- bambootracker-0.4.5+git20121209/BambooTracker/io/pps_io.hpp 1970-01-01 00:00:00.000000000 +0000 +++ bambootracker-0.4.6/BambooTracker/io/pps_io.hpp 2021-02-11 14:37:17.000000000 +0000 @@ -0,0 +1,42 @@ +/* + * Copyright (C) 2021 Rerrah + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +#pragma once + +#include "bank_io.hpp" + +namespace io +{ +class PpsIO final : public AbstractBankIO +{ +public: + PpsIO(); + AbstractBank* load(const BinaryContainer& ctr) const override; + + static AbstractInstrument* loadInstrument(const std::vector& sample, + std::weak_ptr instMan, + int instNum); +}; +} diff -Nru bambootracker-0.4.5+git20121209/BambooTracker/io/pvi_io.cpp bambootracker-0.4.6/BambooTracker/io/pvi_io.cpp --- bambootracker-0.4.5+git20121209/BambooTracker/io/pvi_io.cpp 2020-12-08 14:05:28.000000000 +0000 +++ bambootracker-0.4.6/BambooTracker/io/pvi_io.cpp 2021-02-11 14:37:17.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (C) 2020 Rerrah + * Copyright (C) 2020-2021 Rerrah * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation @@ -25,11 +25,8 @@ #include "pvi_io.hpp" #include -#include #include "instrument.hpp" #include "file_io_error.hpp" -#include "io_utils.hpp" -#include "misc.hpp" namespace io { @@ -37,24 +34,40 @@ AbstractBank* PviIO::load(const BinaryContainer& ctr) const { - size_t globCsr = 0; - if (ctr.readString(globCsr, 4) != "PVI2") - throw FileCorruptionError(FileType::Bank, globCsr); - globCsr += 0x10; + std::string ident = ctr.readString(0, 4); + if (ident != "PVI1" && ident != "PVI2") + throw FileCorruptionError(FileType::Bank, 0); + uint16_t deltaN = ctr.readUint16(8); + uint8_t cnt = ctr.readUint8(11); - size_t sampOffs = globCsr + 128 * 4; - if (ctr.size() < sampOffs) throw FileCorruptionError(FileType::Bank, globCsr); + constexpr size_t sampOffs = 0x10 + 128 * 4; + if (ctr.size() < sampOffs) throw FileCorruptionError(FileType::Bank, 0x10); std::vector ids; std::vector> samples; - extractADPCMSamples(ctr, globCsr, sampOffs, 128, ids, samples); + size_t offs = 0; + size_t addrPos = 0x10; + for (size_t i = 0; i < cnt; ++i) { + uint16_t start = ctr.readUint16(addrPos); + addrPos += 2; + uint16_t stop = ctr.readUint16(addrPos); + addrPos += 2; + + if (start < stop) { + if (ids.empty()) offs = start; + ids.push_back(static_cast(i)); + size_t st = sampOffs + static_cast((start - offs) << 5); + size_t sampSize = std::min((stop + 1u - start) << 5, ctr.size() - st); + samples.push_back(ctr.getSubcontainer(st, sampSize).toVector()); + } + } + /* if (ids.size() != cnt) throw FileCorruptionError(FileType::Bank, 11); */ - return new PviBank(std::move(ids), std::move(samples)); + return new PviBank(ids, deltaN, samples); } -AbstractInstrument* PviIO::loadInstrument(const std::vector sample, - std::weak_ptr instMan, - int instNum) +AbstractInstrument* PviIO::loadInstrument(const std::vector& sample, uint16_t deltaN, + std::weak_ptr instMan, int instNum) { std::shared_ptr instManLocked = instMan.lock(); int sampIdx = instManLocked->findFirstAssignableSampleADPCM(); @@ -65,7 +78,7 @@ instManLocked->storeSampleADPCMRawSample(sampIdx, sample); instManLocked->setSampleADPCMRootKeyNumber(sampIdx, 60); // o5c - instManLocked->setSampleADPCMRootDeltaN(sampIdx, calcADPCMDeltaN(16000)); + instManLocked->setSampleADPCMRootDeltaN(sampIdx, deltaN); return adpcm; } diff -Nru bambootracker-0.4.5+git20121209/BambooTracker/io/pvi_io.hpp bambootracker-0.4.6/BambooTracker/io/pvi_io.hpp --- bambootracker-0.4.5+git20121209/BambooTracker/io/pvi_io.hpp 2020-12-08 14:05:28.000000000 +0000 +++ bambootracker-0.4.6/BambooTracker/io/pvi_io.hpp 2021-02-11 14:37:17.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (C) 2020 Rerrah + * Copyright (C) 2020-2021 Rerrah * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation @@ -35,7 +35,7 @@ PviIO(); AbstractBank* load(const BinaryContainer& ctr) const override; - static AbstractInstrument* loadInstrument(const std::vector sample, + static AbstractInstrument* loadInstrument(const std::vector& sample, uint16_t deltaN, std::weak_ptr instMan, int instNum); }; diff -Nru bambootracker-0.4.5+git20121209/BambooTracker/io/pzi_io.cpp bambootracker-0.4.6/BambooTracker/io/pzi_io.cpp --- bambootracker-0.4.5+git20121209/BambooTracker/io/pzi_io.cpp 1970-01-01 00:00:00.000000000 +0000 +++ bambootracker-0.4.6/BambooTracker/io/pzi_io.cpp 2021-02-11 14:37:17.000000000 +0000 @@ -0,0 +1,107 @@ +/* + * Copyright (C) 2021 Rerrah + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +#include "pzi_io.hpp" +#include +#include +#include "instrument.hpp" +#include "file_io_error.hpp" +#include "chip/codec/ymb_codec.hpp" + +namespace io +{ +PziIO::PziIO() : AbstractBankIO("pzi", "FMP PZI", true, false) {} + +AbstractBank* PziIO::load(const BinaryContainer& ctr) const +{ + if (ctr.readString(0, 4) != "PZI1") + throw FileCorruptionError(FileType::Bank, 0); + + constexpr size_t SAMP_OFFS = 0x920; + if (ctr.size() < SAMP_OFFS) throw FileCorruptionError(FileType::Bank, 0x20); + + std::vector ids; + std::vector> samples; + std::vector isRepeatedList; + std::vector deltaNs; + size_t globCsr = 0x20; + constexpr int MAX_CNT = 128; + for (int i = 0; i < MAX_CNT; ++i) { + uint32_t start = ctr.readUint32(globCsr); + globCsr += 4; + uint32_t len = ctr.readUint32(globCsr); + globCsr += 4; + uint32_t loopStart = ctr.readUint32(globCsr); + globCsr += 4; + uint32_t loopEnd = ctr.readUint32(globCsr); + globCsr += 4; + uint16_t sr = ctr.readUint16(globCsr); + globCsr += 2; + + // only support loop within entire region + bool isRepeated = (!loopStart && len == loopEnd); + + if (len) { + ids.push_back(i); + isRepeatedList.push_back(isRepeated); + deltaNs.push_back(SampleADPCM::calculateADPCMDeltaN(sr)); + + std::vector&& smp = ctr.getSubcontainer(SAMP_OFFS + start, len).toVector(); + std::vector buf(smp.size()); + std::transform(smp.begin(), smp.end(), buf.begin(), [](uint8_t v) { + // Centering + return (static_cast(v) - std::numeric_limits::max()) << 8; + }); + smp.resize((smp.size() + 1) / 2); + smp.shrink_to_fit(); + smp.back() = 0; // Clear last data + codec::ymb_encode(buf.data(), smp.data(), buf.size()); + samples.push_back(std::move(smp)); + } + } + + return new PziBank(ids, deltaNs, isRepeatedList, samples); +} + +AbstractInstrument* PziIO::loadInstrument(const std::vector& sample, + int deltaN, bool isRepeated, + std::weak_ptr instMan, + int instNum) +{ + std::shared_ptr instManLocked = instMan.lock(); + int sampIdx = instManLocked->findFirstAssignableSampleADPCM(); + if (sampIdx < 0) throw FileCorruptionError(FileType::Bank, 0); + + InstrumentADPCM* adpcm = new InstrumentADPCM(instNum, "", instManLocked.get()); + adpcm->setSampleNumber(sampIdx); + + instManLocked->storeSampleADPCMRawSample(sampIdx, sample); + instManLocked->setSampleADPCMRootKeyNumber(sampIdx, 67); // o5g + instManLocked->setSampleADPCMRootDeltaN(sampIdx, deltaN); + instManLocked->setSampleADPCMRepeatEnabled(sampIdx, isRepeated); + + return adpcm; +} +} diff -Nru bambootracker-0.4.5+git20121209/BambooTracker/io/pzi_io.hpp bambootracker-0.4.6/BambooTracker/io/pzi_io.hpp --- bambootracker-0.4.5+git20121209/BambooTracker/io/pzi_io.hpp 1970-01-01 00:00:00.000000000 +0000 +++ bambootracker-0.4.6/BambooTracker/io/pzi_io.hpp 2021-02-11 14:37:17.000000000 +0000 @@ -0,0 +1,43 @@ +/* + * Copyright (C) 2021 Rerrah + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +#pragma once + +#include "bank_io.hpp" + +namespace io +{ +class PziIO final : public AbstractBankIO +{ +public: + PziIO(); + AbstractBank* load(const BinaryContainer& ctr) const override; + + static AbstractInstrument* loadInstrument(const std::vector& sample, + int deltaN, bool isRepeated, + std::weak_ptr instMan, + int instNum); +}; +} diff -Nru bambootracker-0.4.5+git20121209/BambooTracker/io/wav_container.cpp bambootracker-0.4.6/BambooTracker/io/wav_container.cpp --- bambootracker-0.4.5+git20121209/BambooTracker/io/wav_container.cpp 2020-12-08 14:05:28.000000000 +0000 +++ bambootracker-0.4.6/BambooTracker/io/wav_container.cpp 2021-02-11 14:37:17.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (C) 2020 Rerrah + * Copyright (C) 2020-2021 Rerrah * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation @@ -27,35 +27,77 @@ #include #include #include "file_io_error.hpp" -#include "misc.hpp" +namespace io +{ namespace { inline void assertValue(bool f, size_t pos) { if (!f) throw io::FileCorruptionError(io::FileType::WAV, pos); } -} -namespace io +enum WavOffset : size_t { -WavContainer::WavContainer(size_t defCapacity, uint32_t rate, uint16_t nCh, uint16_t bitSize) + RIFF_OFFS = 0, + FILE_SIZE_OFFS = 4, + WAVE_OFFS = 8, + FMT_OFS = 12, + FMT_SIZE_OFFS = 16, + FORMAT_OFFS = 20, + NCH_OFFS = 22, + RATE_OFFS = 24, + BYTE_RATE_OFFS = 28, + BLOCK_SIZE_OFFS = 32, + BIT_SIZE_OFFS = 34, + DATA_OFFS = 36, + DATA_SIZE_OFFS = 40, + PREPARED_SIZE = 44 +}; +} + +WavContainer::WavContainer(uint32_t rate, uint16_t nCh, uint16_t bitSize) : nCh_(nCh), bitSize_(bitSize), - rate_(rate), - buf_(defCapacity) + rate_(rate) { + // RIFF header + buf_.appendString("RIFF"); + uint16_t byteSize = bitSize_ / 8; + buf_.appendUint32(36); + buf_.appendString("WAVE"); + + // fmt chunk + buf_.appendString("fmt "); + uint32_t chunkOfs = 16; + buf_.appendUint32(chunkOfs); + uint16_t fmtId = 1; // Raw linear PCM + buf_.appendUint16(fmtId); + buf_.appendUint16(nCh_); + buf_.appendUint32(rate_); + uint16_t blockSize = byteSize * nCh_; + uint32_t byteRate = blockSize * rate_; + buf_.appendUint32(byteRate); + buf_.appendUint16(blockSize); + buf_.appendUint16(bitSize_); + + // Data chunk + buf_.appendString("data"); + buf_.appendUint32(0); } WavContainer::WavContainer(const BinaryContainer& bc) { + buf_.resize(PREPARED_SIZE); size_t p = 0; assertValue(bc.readString(p, 4) == "RIFF", p); + buf_.writeString(RIFF_OFFS, "RIFF"); p += 4; uint32_t fileSize = bc.readUint32(p) + 8; assertValue(fileSize == bc.size(), p); p += 4; assertValue(bc.readString(p, 4) == "WAVE", p); + buf_.writeString(WAVE_OFFS, "WAVE"); p += 4; while (p < fileSize) { @@ -63,28 +105,38 @@ p += 4; if (id == "fmt ") { + buf_.writeString(FMT_OFS, "fmt "); uint32_t fmtSize = bc.readUint32(p); + buf_.writeUint32(FMT_SIZE_OFFS, 16); size_t fmtp = p + 4; p = fmtp + fmtSize; assertValue(bc.readUint16(fmtp) == 1, fmtp); // Only support linear PCM + buf_.writeUint16(FORMAT_OFFS, 1); fmtp += 2; nCh_ = bc.readUint16(fmtp); + buf_.writeUint16(NCH_OFFS, nCh_); fmtp += 2; rate_ = bc.readUint32(fmtp); + buf_.writeUint32(RATE_OFFS, rate_); fmtp += 4; - uint32_t byteRate = bc.readUint32(fmtp); + byteRate_ = bc.readUint32(fmtp); + buf_.writeUint32(BYTE_RATE_OFFS, byteRate_); fmtp += 4; - uint16_t blockSize = bc.readUint16(fmtp); - assertValue(byteRate == blockSize * rate_, fmtp); + blockSize_ = bc.readUint16(fmtp); + assertValue(byteRate_ == blockSize_ * rate_, fmtp); + buf_.writeUint16(BLOCK_SIZE_OFFS, blockSize_); fmtp += 2; bitSize_ = bc.readUint16(fmtp); assertValue(bitSize_ == 16, fmtp); // Only support 16-bit - assertValue(blockSize == nCh_ * bitSize_ / 8, fmtp); - fmtp += 2; + assertValue(blockSize_ == nCh_ * bitSize_ / 8, fmtp); + buf_.writeUint16(BIT_SIZE_OFFS, bitSize_); + /* fmtp += 2; */ } else if (id == "data") { + buf_.writeString(DATA_OFFS, "data"); uint32_t dataSize = bc.readUint32(p); assertValue(p + dataSize <= bc.size(), p); + buf_.writeUint32(DATA_SIZE_OFFS, dataSize); p += 4; buf_.appendBinaryContainer(bc.getSubcontainer(p, dataSize)); p += dataSize; @@ -93,97 +145,60 @@ p += (bc.readUint32(p) + 4); // Jump to next chunk } } -} -void WavContainer::setChannelCount(uint16_t n) noexcept -{ - nCh_ = n; + buf_.writeUint32(FILE_SIZE_OFFS, buf_.size() - 8); } -uint16_t WavContainer::getChannelCount() const noexcept +void WavContainer::setChannelCount(uint16_t n) { - return nCh_; + nCh_ = n; + buf_.writeUint16(NCH_OFFS, nCh_); + updateBlockSize(); } -void WavContainer::setBitSize(uint16_t size) noexcept +void WavContainer::setBitSize(uint16_t size) { bitSize_ = size; + buf_.writeUint16(BIT_SIZE_OFFS, bitSize_); + updateBlockSize(); } -uint16_t WavContainer::getBitSize() const noexcept -{ - return bitSize_; -} - -void WavContainer::setSampleRate(uint32_t rate) noexcept +void WavContainer::setSampleRate(uint32_t rate) { rate_ = rate; + buf_.writeUint16(rate, rate); + updateBlockSize(); + updateByteRate(); } -uint32_t WavContainer::getSampleRate() const noexcept +WavContainer::size_type WavContainer::getSampleCount() const { - return rate_; + return (buf_.size() - PREPARED_SIZE) * bitSize_ / 8 / nCh_; } -size_t WavContainer::getSampleCount() const -{ - return buf_.size() * bitSize_ / 8 / nCh_; -} - -void WavContainer::appendSample(const int16_t* sample, size_t nSamples) +void WavContainer::appendSample(const int16_t* sample, size_type nSamples) { size_t dataSize = nCh_ * nSamples * sizeof(int16_t); buf_.appendArray(reinterpret_cast(sample), dataSize); + updateSizeDataAfterAppendSample(); } void WavContainer::appendSample(const std::vector& sample) { size_t dataSize = sample.size() * sizeof(int16_t); buf_.appendArray(reinterpret_cast(sample.data()), dataSize); + updateSizeDataAfterAppendSample(); } void WavContainer::appendSample(const BinaryContainer& sample) { buf_.appendBinaryContainer(sample); + updateSizeDataAfterAppendSample(); } BinaryContainer WavContainer::getSample() const noexcept { - return buf_; -} - -BinaryContainer WavContainer::createWavBinary() -{ - BinaryContainer bc; - - // RIFF header - bc.appendString("RIFF"); - uint16_t byteSize = bitSize_ / 8; - uint32_t dataSize = buf_.size() / byteSize; - uint32_t offset = dataSize + 36; - bc.appendUint32(offset); - bc.appendString("WAVE"); - - // fmt chunk - bc.appendString("fmt "); - uint32_t chunkOfs = 16; - bc.appendUint32(chunkOfs); - uint16_t fmtId = 1; // Raw linear PCM - bc.appendUint16(fmtId); - bc.appendUint16(nCh_); - bc.appendUint32(rate_); - uint16_t blockSize = byteSize * nCh_; - uint32_t byteRate = blockSize * rate_; - bc.appendUint32(byteRate); - bc.appendUint16(blockSize); - bc.appendUint16(bitSize_); - - // Data chunk - bc.appendString("data"); - bc.appendUint32(buf_.size()); - bc.appendBinaryContainer(buf_); - - return bc; + return buf_.getSubcontainer(PREPARED_SIZE, buf_.size() - PREPARED_SIZE); } // WavContainer* WavContainer::resample(const WavContainer* src, uint32_t rate) @@ -239,4 +254,22 @@ // return tgt.release(); // } + +void WavContainer::updateBlockSize() +{ + blockSize_ = nCh_ * bitSize_ / 8; + buf_.writeUint16(BLOCK_SIZE_OFFS, blockSize_); +} + +void WavContainer::updateByteRate() +{ + byteRate_ = blockSize_ * rate_; + buf_.writeUint32(BYTE_RATE_OFFS, byteRate_); +} + +void WavContainer::updateSizeDataAfterAppendSample() +{ + buf_.writeUint32(FILE_SIZE_OFFS, buf_.size() - 8); + buf_.writeUint32(DATA_SIZE_OFFS, buf_.size() - PREPARED_SIZE); +} } diff -Nru bambootracker-0.4.5+git20121209/BambooTracker/io/wav_container.hpp bambootracker-0.4.6/BambooTracker/io/wav_container.hpp --- bambootracker-0.4.5+git20121209/BambooTracker/io/wav_container.hpp 2020-12-08 14:05:28.000000000 +0000 +++ bambootracker-0.4.6/BambooTracker/io/wav_container.hpp 2021-02-11 14:37:17.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (C) 2020 Rerrah + * Copyright (C) 2020-2021 Rerrah * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation @@ -34,31 +34,60 @@ class WavContainer { public: - explicit WavContainer(size_t defCapacity = 0, uint32_t rate = 44100, uint16_t nCh = 2, uint16_t getBitSize = 16); + using value_type = BinaryContainer::value_type; + using size_type = BinaryContainer::size_type; + using iterator = BinaryContainer::iterator; + using const_iterator = BinaryContainer::const_iterator; + using reverse_iterator = BinaryContainer::reverse_iterator; + using const_reverse_iterator = BinaryContainer::const_reverse_iterator; + + explicit WavContainer(uint32_t rate = 44100, uint16_t nCh = 2, uint16_t getBitSize = 16); explicit WavContainer(const BinaryContainer& bc); - void setChannelCount(uint16_t n) noexcept; - uint16_t getChannelCount() const noexcept; - void setBitSize(uint16_t size) noexcept; - uint16_t getBitSize() const noexcept; - void setSampleRate(uint32_t rate) noexcept; - uint32_t getSampleRate() const noexcept; + inline iterator begin() noexcept { return buf_.begin(); } + inline const_iterator begin() const noexcept { return buf_.begin(); } + + inline iterator end() noexcept { return buf_.end(); } + inline const_iterator end() const noexcept { return buf_.end(); } + + inline const_iterator cbegin() const noexcept { return buf_.cbegin(); } + inline const_iterator cend() const noexcept { return buf_.cend(); } + + inline reverse_iterator rbegin() noexcept { return buf_.rbegin(); } + inline const_reverse_iterator rbegin() const noexcept { return buf_.rbegin(); } + + inline reverse_iterator rend() noexcept { return buf_.rend(); } + inline const_reverse_iterator rend() const noexcept { return buf_.rend(); } - size_t getSampleCount() const; + inline const_reverse_iterator crbegin() const noexcept { return buf_.crbegin(); } + inline const_reverse_iterator crend() const noexcept { return buf_.crend(); } - void appendSample(const int16_t* sample, size_t nSamples); + inline size_type size() const { return buf_.size(); } + + void setChannelCount(uint16_t n); + inline uint16_t getChannelCount() const noexcept { return nCh_; } + void setBitSize(uint16_t size); + inline uint16_t getBitSize() const noexcept { return bitSize_; } + void setSampleRate(uint32_t rate); + inline uint32_t getSampleRate() const noexcept { return rate_; } + + size_type getSampleCount() const; + + void appendSample(const int16_t* sample, size_type nSamples); void appendSample(const std::vector& sample); void appendSample(const BinaryContainer& sample); BinaryContainer getSample() const noexcept; - BinaryContainer createWavBinary(); - // static WavContainer* resample(const WavContainer* src, uint32_t rate); // static WavContainer* mono(const WavContainer* src); private: - uint16_t nCh_, bitSize_; - uint32_t rate_; + uint16_t nCh_, bitSize_, blockSize_; + uint32_t rate_, byteRate_; BinaryContainer buf_; + + void updateBlockSize(); + void updateByteRate(); + void updateSizeDataAfterAppendSample(); }; } diff -Nru bambootracker-0.4.5+git20121209/BambooTracker/io/wopn_io.cpp bambootracker-0.4.6/BambooTracker/io/wopn_io.cpp --- bambootracker-0.4.5+git20121209/BambooTracker/io/wopn_io.cpp 2020-12-08 14:05:28.000000000 +0000 +++ bambootracker-0.4.6/BambooTracker/io/wopn_io.cpp 2021-02-11 14:37:17.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (C) 2020 Rerrah + * Copyright (C) 2020-2021 Rerrah * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation @@ -38,7 +38,8 @@ }; std::unique_ptr wopn; - wopn.reset(WOPN_LoadBankFromMem(const_cast(ctr.getPointer()), ctr.size(), nullptr)); + auto&& mem = ctr.toVector(); + wopn.reset(WOPN_LoadBankFromMem(mem.data(), mem.size(), nullptr)); if (!wopn) throw FileCorruptionError(FileType::Bank, 0); diff -Nru bambootracker-0.4.5+git20121209/BambooTracker/jamming.cpp bambootracker-0.4.6/BambooTracker/jamming.cpp --- bambootracker-0.4.5+git20121209/BambooTracker/jamming.cpp 2020-12-08 14:05:28.000000000 +0000 +++ bambootracker-0.4.6/BambooTracker/jamming.cpp 2021-02-11 14:37:17.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (C) 2018-2020 Rerrah + * Copyright (C) 2018-2021 Rerrah * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation @@ -25,126 +25,88 @@ #include "jamming.hpp" #include +#include #include #include +#include "note.hpp" +#include "utils.hpp" namespace jam_utils { -Note jamKeyToNote(JamKey &key) +Note makeNote(const JamKeyInfo& info, int baseOctave) { - switch (key) { - case JamKey::LowC: - case JamKey::LowC2: - case JamKey::HighC: - case JamKey::HighC2: return Note::C; - case JamKey::LowCS: - case JamKey::LowCS2: - case JamKey::HighCS: - case JamKey::HighCS2: return Note::CS; - case JamKey::LowD: - case JamKey::LowD2: - case JamKey::HighD: - case JamKey::HighD2: return Note::D; - case JamKey::LowDS: - case JamKey::HighDS: return Note::DS; - case JamKey::LowE: - case JamKey::HighE: return Note::E; - case JamKey::LowF: - case JamKey::HighF: return Note::F; - case JamKey::LowFS: - case JamKey::HighFS: return Note::FS; - case JamKey::LowG: - case JamKey::HighG: return Note::G; - case JamKey::LowGS: - case JamKey::HighGS: return Note::GS; - case JamKey::LowA: - case JamKey::HighA: return Note::A; - case JamKey::LowAS: - case JamKey::HighAS: return Note::AS; - case JamKey::LowB: - case JamKey::HighB: return Note::B; - default: throw std::invalid_argument("Unexpected JamKey."); - } + return (info.key == JamKey::MidiKey) ? Note(info.keyNum) : makeNote(baseOctave, info.key); } -JamKey noteToJamKey(Note& note) -{ - switch (note) { - case Note::C: return JamKey::LowC; - case Note::CS: return JamKey::LowCS; - case Note::D: return JamKey::LowD; - case Note::DS: return JamKey::LowDS; - case Note::E: return JamKey::LowE; - case Note::F: return JamKey::LowF; - case Note::FS: return JamKey::LowFS; - case Note::G: return JamKey::LowG; - case Note::GS: return JamKey::LowGS; - case Note::A: return JamKey::LowA; - case Note::AS: return JamKey::LowAS; - case Note::B: return JamKey::LowB; - default: throw std::invalid_argument("Unexpected Note."); - } -} - -int calculateJamKeyOctave(int baseOctave, JamKey &key) +Note makeNote(int baseOctave, JamKey key) { switch (key) { case JamKey::LowC: + return Note(baseOctave, Note::C); case JamKey::LowCS: + return Note(baseOctave, Note::CS); case JamKey::LowD: + return Note(baseOctave, Note::D); case JamKey::LowDS: + return Note(baseOctave, Note::DS); case JamKey::LowE: + return Note(baseOctave, Note::E); case JamKey::LowF: + return Note(baseOctave, Note::F); case JamKey::LowFS: + return Note(baseOctave, Note::FS); case JamKey::LowG: + return Note(baseOctave, Note::G); case JamKey::LowGS: + return Note(baseOctave, Note::GS); case JamKey::LowA: + return Note(baseOctave, Note::A); case JamKey::LowAS: - case JamKey::LowB: return baseOctave; + return Note(baseOctave, Note::AS); + case JamKey::LowB: + return Note(baseOctave, Note::B); case JamKey::LowC2: - case JamKey::LowCS2: - case JamKey::LowD2: case JamKey::HighC: + return Note(baseOctave + 1, Note::C); + case JamKey::LowCS2: case JamKey::HighCS: + return Note(baseOctave + 1, Note::CS); + case JamKey::LowD2: case JamKey::HighD: + return Note(baseOctave + 1, Note::D); case JamKey::HighDS: + return Note(baseOctave + 1, Note::DS); case JamKey::HighE: + return Note(baseOctave + 1, Note::E); case JamKey::HighF: + return Note(baseOctave + 1, Note::F); case JamKey::HighFS: + return Note(baseOctave + 1, Note::FS); case JamKey::HighG: + return Note(baseOctave + 1, Note::G); case JamKey::HighGS: + return Note(baseOctave + 1, Note::GS); case JamKey::HighA: + return Note(baseOctave + 1, Note::A); case JamKey::HighAS: - case JamKey::HighB: return (baseOctave + 1); + return Note(baseOctave + 1, Note::AS); + case JamKey::HighB: + return Note(baseOctave + 1, Note::B); case JamKey::HighC2: + return Note(baseOctave + 2, Note::C); case JamKey::HighCS2: - case JamKey::HighD2: return (baseOctave + 2); - default: throw std::invalid_argument("Unexpected JamKey."); + return Note(baseOctave + 2, Note::CS); + case JamKey::HighD2: + return Note(baseOctave + 2, Note::D); + default: + throw std::invalid_argument("invalid jam key"); } } } JamManager::JamManager() - : isJamMode_(true), - isPoly_(true) -{ - reset(); -} - -bool JamManager::toggleJamMode() -{ - isJamMode_ = !isJamMode_; - return isJamMode_; -} - -bool JamManager::isJamMode() const noexcept + : isJamMode_(true), isPoly_(true) { - return isJamMode_; -} - -void JamManager::polyphonic(bool flag) -{ - isPoly_ = flag; reset(); } @@ -162,8 +124,7 @@ keyDataList.push_back(onData); } else { - auto&& it = std::find_if(keyOnTable_.begin(), - keyOnTable_.end(), + auto&& it = utils::findIf(keyOnTable_, [&](JamKeyInfo x) { return (x.source == source && x.key == key); }); if (it == keyOnTable_.end()) { if (isPoly_) onData.channelInSource = unusedCh.front(); @@ -182,9 +143,7 @@ } } else { - auto&& it = std::find_if(keyOnTable_.begin(), - keyOnTable_.end(), - [&](JamKeyInfo x) { return (x.source == source); }); + auto&& it = utils::findIf(keyOnTable_, [&](JamKeyInfo x) { return (x.source == source); }); JamKeyInfo del = *it; if (isPoly_) onData.channelInSource = del.channelInSource; keyDataList.push_back(onData); @@ -203,7 +162,7 @@ auto cond = (key == JamKey::MidiKey) ? std::function([&](JamKeyInfo x) -> bool { return (x.key == JamKey::MidiKey && x.keyNum == keyNum); }) : std::function([&](JamKeyInfo x) -> bool { return (x.key == key); }); - auto&& it = std::find_if(keyOnTable_.begin(), keyOnTable_.end(), cond); + auto&& it = utils::findIf(keyOnTable_, cond); if (it == keyOnTable_.end()) { keyData.channelInSource = -1; // Already released } @@ -225,10 +184,7 @@ unusedCh_[SoundSource::FM] = std::deque(6); unusedCh_[SoundSource::SSG] = std::deque(3); unusedCh_[SoundSource::ADPCM] = std::deque(1); - - for (auto& pair : unusedCh_) { - std::generate(pair.second.begin(), pair.second.end(), [i = 0]() mutable -> int { return i++; }); - } + for (auto& pair : unusedCh_) std::iota(pair.second.begin(), pair.second.end(), 0); } else { for (auto& pair : unusedCh_) pair.second.resize(1); diff -Nru bambootracker-0.4.5+git20121209/BambooTracker/jamming.hpp bambootracker-0.4.6/BambooTracker/jamming.hpp --- bambootracker-0.4.5+git20121209/BambooTracker/jamming.hpp 2020-12-08 14:05:28.000000000 +0000 +++ bambootracker-0.4.6/BambooTracker/jamming.hpp 2021-02-11 14:37:17.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (C) 2018-2020 Rerrah + * Copyright (C) 2018-2021 Rerrah * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation @@ -29,16 +29,30 @@ #include #include #include "enum_hash.hpp" -#include "misc.hpp" +#include "bamboo_tracker_defs.hpp" -struct JamKeyInfo; -enum class JamKey; +class Note; + +enum class JamKey +{ + LowC, LowCS, LowD, LowDS, LowE, LowF, LowFS, LowG, + LowGS, LowA, LowAS, LowB, LowC2, LowCS2, LowD2, + HighC, HighCS, HighD, HighDS, HighE, HighF, HighFS, HighG, + HighGS, HighA, HighAS, HighB, HighC2, HighCS2, HighD2, MidiKey +}; + +struct JamKeyInfo +{ + JamKey key; + int channelInSource; + SoundSource source; + int keyNum; +}; namespace jam_utils { -Note jamKeyToNote(JamKey& key); -JamKey noteToJamKey(Note& note); -int calculateJamKeyOctave(int baseOctave, JamKey& key); +Note makeNote(const JamKeyInfo& info, int baseOctave); +Note makeNote(int baseOctave, JamKey key); } class JamManager @@ -46,7 +60,7 @@ public: JamManager(); bool toggleJamMode(); - bool isJamMode() const noexcept; + bool isJamMode() const noexcept { return isJamMode_; } void polyphonic(bool flag); std::vector keyOn(JamKey key, int channel, SoundSource source, int keyNum); JamKeyInfo keyOff(JamKey key, int keyNum); @@ -59,18 +73,15 @@ std::unordered_map> unusedCh_; }; -struct JamKeyInfo +//=============================================== +inline bool JamManager::toggleJamMode() { - JamKey key; - int channelInSource; - SoundSource source; - int keyNum; -}; + isJamMode_ = !isJamMode_; + return isJamMode_; +} -enum class JamKey +inline void JamManager::polyphonic(bool flag) { - LowC, LowCS, LowD, LowDS, LowE, LowF, LowFS, LowG, - LowGS, LowA, LowAS, LowB, LowC2, LowCS2, LowD2, - HighC, HighCS, HighD, HighDS, HighE, HighF, HighFS, HighG, - HighGS, HighA, HighAS, HighB, HighC2, HighCS2, HighD2, MidiKey -}; + isPoly_ = flag; + reset(); +} diff -Nru bambootracker-0.4.5+git20121209/BambooTracker/main.cpp bambootracker-0.4.6/BambooTracker/main.cpp --- bambootracker-0.4.5+git20121209/BambooTracker/main.cpp 2020-12-08 14:05:28.000000000 +0000 +++ bambootracker-0.4.6/BambooTracker/main.cpp 2021-02-11 14:37:17.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (C) 2018-2020 Rerrah + * Copyright (C) 2018-2021 Rerrah * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation @@ -38,16 +38,19 @@ #include "gui/q_application_wrapper.hpp" #include "gui/configuration_handler.hpp" +namespace +{ // Localization static void setupTranslations(); static QString findQtTranslationsDir(); static QString findAppTranslationsDir(); +} int main(int argc, char* argv[]) { try { std::shared_ptr config = std::make_shared(); - ConfigurationHandler::loadConfiguration(config); + io::loadConfiguration(config); #if QT_VERSION >= QT_VERSION_CHECK(5, 6, 0) QApplication::setAttribute(Qt::AA_EnableHighDpiScaling); @@ -63,7 +66,7 @@ w->show(); int ret = a->exec(); - ConfigurationHandler::saveConfiguration(config); + io::saveConfiguration(config); if (ret) QMessageBox::critical(nullptr, QObject::tr("Error"), QObject::tr("An unknown error occurred.")); return ret; @@ -75,8 +78,10 @@ } } +namespace +{ // Sets up the translation according to the current language -static void setupTranslations() +void setupTranslations() { QApplication *a = qApp; const QString lang = QLocale::system().name(); @@ -104,7 +109,7 @@ } // Finds the location of Qt translation catalogs -static QString findQtTranslationsDir() +QString findQtTranslationsDir() { #if defined(Q_OS_DARWIN) // if this is macOS, attempt to load from inside an app bundle @@ -123,7 +128,7 @@ } // Finds the location of our translation catalogs -static QString findAppTranslationsDir() +QString findAppTranslationsDir() { #ifndef QT_NO_DEBUG // if this is a debug build, attempt to load from the source directory @@ -146,3 +151,4 @@ return QApplication::applicationDirPath() + "/../share/BambooTracker/lang"; #endif } +} diff -Nru bambootracker-0.4.5+git20121209/BambooTracker/midi/midi.cpp bambootracker-0.4.6/BambooTracker/midi/midi.cpp --- bambootracker-0.4.5+git20121209/BambooTracker/midi/midi.cpp 2020-12-08 14:05:28.000000000 +0000 +++ bambootracker-0.4.6/BambooTracker/midi/midi.cpp 2021-02-11 14:37:17.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (C) 2019-2020 Rerrah + * Copyright (C) 2019-2021 Rerrah * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation @@ -25,8 +25,8 @@ #include "midi.hpp" #include -#include -#include "RtMidi/RtMidi.hpp" +#include "RtMidi.h" +#include "utils.hpp" namespace { @@ -77,7 +77,7 @@ bool MidiInterface::isAvailableApi(const std::string& api) const { const std::vector apis = getAvailableApis(); - return (std::find(apis.begin(), apis.end(), api) != apis.end()); + return (utils::find(apis, api) != apis.end()); } bool MidiInterface::switchApi(std::string api, std::string* errDetail) @@ -213,19 +213,19 @@ } try { - RtMidiIn &client = *inputClient_; - closeInputPort(); + RtMidiIn &client = *inputClient_; + closeInputPort(); - if (port == ~0u) { - client.openVirtualPort(MIDI_INP_PORT_NAME); - hasOpenInputPort_ = true; - } - else { - client.openPort(port, MIDI_INP_PORT_NAME); - hasOpenInputPort_ = client.isPortOpen(); - } - if (errDetail) *errDetail = ""; - return true; + if (port == ~0u) { + client.openVirtualPort(MIDI_INP_PORT_NAME); + hasOpenInputPort_ = true; + } + else { + client.openPort(port, MIDI_INP_PORT_NAME); + hasOpenInputPort_ = client.isPortOpen(); + } + if (errDetail) *errDetail = ""; + return true; } catch (RtMidiError& error) { if (errDetail) *errDetail = error.getMessage(); diff -Nru bambootracker-0.4.5+git20121209/BambooTracker/midi/midi.hpp bambootracker-0.4.6/BambooTracker/midi/midi.hpp --- bambootracker-0.4.5+git20121209/BambooTracker/midi/midi.hpp 2020-12-08 14:05:28.000000000 +0000 +++ bambootracker-0.4.6/BambooTracker/midi/midi.hpp 2021-02-11 14:37:17.000000000 +0000 @@ -24,11 +24,13 @@ */ #pragma once + #include #include #include #include #include +#include "RtMidi.h" class RtMidiIn; diff -Nru bambootracker-0.4.5+git20121209/BambooTracker/midi/RtMidi/RtMidi.cpp bambootracker-0.4.6/BambooTracker/midi/RtMidi/RtMidi.cpp --- bambootracker-0.4.5+git20121209/BambooTracker/midi/RtMidi/RtMidi.cpp 2020-12-08 14:05:28.000000000 +0000 +++ bambootracker-0.4.6/BambooTracker/midi/RtMidi/RtMidi.cpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,3445 +0,0 @@ -/**********************************************************************/ -/*! \class RtMidi - \brief An abstract base class for realtime MIDI input/output. - - This class implements some common functionality for the realtime - MIDI input/output subclasses RtMidiIn and RtMidiOut. - - RtMidi GitHub site: https://github.com/thestk/rtmidi - RtMidi WWW site: http://www.music.mcgill.ca/~gary/rtmidi/ - - RtMidi: realtime MIDI i/o C++ classes - Copyright (c) 2003-2019 Gary P. Scavone - - Permission is hereby granted, free of charge, to any person - obtaining a copy of this software and associated documentation files - (the "Software"), to deal in the Software without restriction, - including without limitation the rights to use, copy, modify, merge, - publish, distribute, sublicense, and/or sell copies of the Software, - and to permit persons to whom the Software is furnished to do so, - subject to the following conditions: - - The above copyright notice and this permission notice shall be - included in all copies or substantial portions of the Software. - - Any person wishing to distribute modifications to the Software is - asked to send the modifications to the original developer so that - they can be incorporated into the canonical version. This is, - however, not a binding provision of this license. - - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. - IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR - ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF - CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION - WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -*/ -/**********************************************************************/ - -#include "RtMidi.hpp" -#include - -#if defined(__MACOSX_CORE__) - #if TARGET_OS_IPHONE - #define AudioGetCurrentHostTime CAHostTimeBase::GetCurrentTime - #define AudioConvertHostTimeToNanos CAHostTimeBase::ConvertToNanos - #endif -#endif - -// Default for Windows is to add an identifier to the port names; this -// flag can be defined (e.g. in your project file) to disable this behaviour. -//#define RTMIDI_DO_NOT_ENSURE_UNIQUE_PORTNAMES - -// **************************************************************** // -// -// MidiInApi and MidiOutApi subclass prototypes. -// -// **************************************************************** // - -#if !defined(__LINUX_ALSA__) && !defined(__UNIX_JACK__) && !defined(__MACOSX_CORE__) && !defined(__WINDOWS_MM__) - #define __RTMIDI_DUMMY__ -#endif - -#if defined(__MACOSX_CORE__) - -class MidiInCore: public MidiInApi -{ - public: - MidiInCore( const std::string &clientName, unsigned int queueSizeLimit ); - ~MidiInCore( void ); - RtMidi::Api getCurrentApi( void ) { return RtMidi::MACOSX_CORE; }; - void openPort( unsigned int portNumber, const std::string &portName ); - void openVirtualPort( const std::string &portName ); - void closePort( void ); - void setClientName( const std::string &clientName ); - void setPortName( const std::string &portName ); - unsigned int getPortCount( void ); - std::string getPortName( unsigned int portNumber ); - - protected: - void initialize( const std::string& clientName ); -}; - -class MidiOutCore: public MidiOutApi -{ - public: - MidiOutCore( const std::string &clientName ); - ~MidiOutCore( void ); - RtMidi::Api getCurrentApi( void ) { return RtMidi::MACOSX_CORE; }; - void openPort( unsigned int portNumber, const std::string &portName ); - void openVirtualPort( const std::string &portName ); - void closePort( void ); - void setClientName( const std::string &clientName ); - void setPortName( const std::string &portName ); - unsigned int getPortCount( void ); - std::string getPortName( unsigned int portNumber ); - void sendMessage( const unsigned char *message, size_t size ); - - protected: - void initialize( const std::string& clientName ); -}; - -#endif - -#if defined(__UNIX_JACK__) - -class MidiInJack: public MidiInApi -{ - public: - MidiInJack( const std::string &clientName, unsigned int queueSizeLimit ); - ~MidiInJack( void ); - RtMidi::Api getCurrentApi( void ) { return RtMidi::UNIX_JACK; }; - void openPort( unsigned int portNumber, const std::string &portName ); - void openVirtualPort( const std::string &portName ); - void closePort( void ); - void setClientName( const std::string &clientName ); - void setPortName( const std::string &portName); - unsigned int getPortCount( void ); - std::string getPortName( unsigned int portNumber ); - - protected: - std::string clientName; - - void connect( void ); - void initialize( const std::string& clientName ); -}; - -class MidiOutJack: public MidiOutApi -{ - public: - MidiOutJack( const std::string &clientName ); - ~MidiOutJack( void ); - RtMidi::Api getCurrentApi( void ) { return RtMidi::UNIX_JACK; }; - void openPort( unsigned int portNumber, const std::string &portName ); - void openVirtualPort( const std::string &portName ); - void closePort( void ); - void setClientName( const std::string &clientName ); - void setPortName( const std::string &portName); - unsigned int getPortCount( void ); - std::string getPortName( unsigned int portNumber ); - void sendMessage( const unsigned char *message, size_t size ); - - protected: - std::string clientName; - - void connect( void ); - void initialize( const std::string& clientName ); -}; - -#endif - -#if defined(__LINUX_ALSA__) - -class MidiInAlsa: public MidiInApi -{ - public: - MidiInAlsa( const std::string &clientName, unsigned int queueSizeLimit ); - ~MidiInAlsa( void ); - RtMidi::Api getCurrentApi( void ) { return RtMidi::LINUX_ALSA; }; - void openPort( unsigned int portNumber, const std::string &portName ); - void openVirtualPort( const std::string &portName ); - void closePort( void ); - void setClientName( const std::string &clientName ); - void setPortName( const std::string &portName); - unsigned int getPortCount( void ); - std::string getPortName( unsigned int portNumber ); - - protected: - void initialize( const std::string& clientName ); -}; - -class MidiOutAlsa: public MidiOutApi -{ - public: - MidiOutAlsa( const std::string &clientName ); - ~MidiOutAlsa( void ); - RtMidi::Api getCurrentApi( void ) { return RtMidi::LINUX_ALSA; }; - void openPort( unsigned int portNumber, const std::string &portName ); - void openVirtualPort( const std::string &portName ); - void closePort( void ); - void setClientName( const std::string &clientName ); - void setPortName( const std::string &portName ); - unsigned int getPortCount( void ); - std::string getPortName( unsigned int portNumber ); - void sendMessage( const unsigned char *message, size_t size ); - - protected: - void initialize( const std::string& clientName ); -}; - -#endif - -#if defined(__WINDOWS_MM__) - -class MidiInWinMM: public MidiInApi -{ - public: - MidiInWinMM( const std::string &clientName, unsigned int queueSizeLimit ); - ~MidiInWinMM( void ); - RtMidi::Api getCurrentApi( void ) { return RtMidi::WINDOWS_MM; }; - void openPort( unsigned int portNumber, const std::string &portName ); - void openVirtualPort( const std::string &portName ); - void closePort( void ); - void setClientName( const std::string &clientName ); - void setPortName( const std::string &portName ); - unsigned int getPortCount( void ); - std::string getPortName( unsigned int portNumber ); - - protected: - void initialize( const std::string& clientName ); -}; - -class MidiOutWinMM: public MidiOutApi -{ - public: - MidiOutWinMM( const std::string &clientName ); - ~MidiOutWinMM( void ); - RtMidi::Api getCurrentApi( void ) { return RtMidi::WINDOWS_MM; }; - void openPort( unsigned int portNumber, const std::string &portName ); - void openVirtualPort( const std::string &portName ); - void closePort( void ); - void setClientName( const std::string &clientName ); - void setPortName( const std::string &portName ); - unsigned int getPortCount( void ); - std::string getPortName( unsigned int portNumber ); - void sendMessage( const unsigned char *message, size_t size ); - - protected: - void initialize( const std::string& clientName ); -}; - -#endif - -#if defined(__RTMIDI_DUMMY__) - -class MidiInDummy: public MidiInApi -{ - public: - MidiInDummy( const std::string &/*clientName*/, unsigned int queueSizeLimit ) : MidiInApi( queueSizeLimit ) { errorString_ = "MidiInDummy: This class provides no functionality."; error( RtMidiError::WARNING, errorString_ ); } - RtMidi::Api getCurrentApi( void ) { return RtMidi::RTMIDI_DUMMY; } - void openPort( unsigned int /*portNumber*/, const std::string &/*portName*/ ) {} - void openVirtualPort( const std::string &/*portName*/ ) {} - void closePort( void ) {} - void setClientName( const std::string &/*clientName*/ ) {}; - void setPortName( const std::string &/*portName*/ ) {}; - unsigned int getPortCount( void ) { return 0; } - std::string getPortName( unsigned int /*portNumber*/ ) { return ""; } - - protected: - void initialize( const std::string& /*clientName*/ ) {} -}; - -class MidiOutDummy: public MidiOutApi -{ - public: - MidiOutDummy( const std::string &/*clientName*/ ) { errorString_ = "MidiOutDummy: This class provides no functionality."; error( RtMidiError::WARNING, errorString_ ); } - RtMidi::Api getCurrentApi( void ) { return RtMidi::RTMIDI_DUMMY; } - void openPort( unsigned int /*portNumber*/, const std::string &/*portName*/ ) {} - void openVirtualPort( const std::string &/*portName*/ ) {} - void closePort( void ) {} - void setClientName( const std::string &/*clientName*/ ) {}; - void setPortName( const std::string &/*portName*/ ) {}; - unsigned int getPortCount( void ) { return 0; } - std::string getPortName( unsigned int /*portNumber*/ ) { return ""; } - void sendMessage( const unsigned char * /*message*/, size_t /*size*/ ) {} - - protected: - void initialize( const std::string& /*clientName*/ ) {} -}; - -#endif - -//*********************************************************************// -// RtMidi Definitions -//*********************************************************************// - -RtMidi :: RtMidi() - : rtapi_(0) -{ -} - -RtMidi :: ~RtMidi() -{ - delete rtapi_; - rtapi_ = 0; -} - -std::string RtMidi :: getVersion( void ) throw() -{ - return std::string( RTMIDI_VERSION ); -} - -// Define API names and display names. -// Must be in same order as API enum. -extern "C" { -const char* rtmidi_api_names[][2] = { - { "unspecified" , "Unknown" }, - { "core" , "CoreMidi" }, - { "alsa" , "ALSA" }, - { "jack" , "Jack" }, - { "winmm" , "Windows MultiMedia" }, - { "dummy" , "Dummy" }, -}; -const unsigned int rtmidi_num_api_names = - sizeof(rtmidi_api_names)/sizeof(rtmidi_api_names[0]); - -// The order here will control the order of RtMidi's API search in -// the constructor. -extern "C" const RtMidi::Api rtmidi_compiled_apis[] = { -#if defined(__MACOSX_CORE__) - RtMidi::MACOSX_CORE, -#endif -#if defined(__LINUX_ALSA__) - RtMidi::LINUX_ALSA, -#endif -#if defined(__UNIX_JACK__) - RtMidi::UNIX_JACK, -#endif -#if defined(__WINDOWS_MM__) - RtMidi::WINDOWS_MM, -#endif -#if defined(__RTMIDI_DUMMY__) - RtMidi::RTMIDI_DUMMY, -#endif - RtMidi::UNSPECIFIED, -}; -extern "C" const unsigned int rtmidi_num_compiled_apis = - sizeof(rtmidi_compiled_apis)/sizeof(rtmidi_compiled_apis[0])-1; -} - -// This is a compile-time check that rtmidi_num_api_names == RtMidi::NUM_APIS. -// If the build breaks here, check that they match. -template class StaticAssert { private: StaticAssert() {} }; -template<> class StaticAssert{ public: StaticAssert() {} }; -class StaticAssertions { StaticAssertions() { - StaticAssert(); -}}; - -void RtMidi :: getCompiledApi( std::vector &apis ) throw() -{ - apis = std::vector(rtmidi_compiled_apis, - rtmidi_compiled_apis + rtmidi_num_compiled_apis); -} - -std::string RtMidi :: getApiName( RtMidi::Api api ) -{ - if (api < 0 || api >= RtMidi::NUM_APIS) - return ""; - return rtmidi_api_names[api][0]; -} - -std::string RtMidi :: getApiDisplayName( RtMidi::Api api ) -{ - if (api < 0 || api >= RtMidi::NUM_APIS) - return "Unknown"; - return rtmidi_api_names[api][1]; -} - -RtMidi::Api RtMidi :: getCompiledApiByName( const std::string &name ) -{ - unsigned int i=0; - for (i = 0; i < rtmidi_num_compiled_apis; ++i) - if (name == rtmidi_api_names[rtmidi_compiled_apis[i]][0]) - return rtmidi_compiled_apis[i]; - return RtMidi::UNSPECIFIED; -} - -void RtMidi :: setClientName( const std::string &clientName ) -{ - rtapi_->setClientName( clientName ); -} - -void RtMidi :: setPortName( const std::string &portName ) -{ - rtapi_->setPortName( portName ); -} - - -//*********************************************************************// -// RtMidiIn Definitions -//*********************************************************************// - -void RtMidiIn :: openMidiApi( RtMidi::Api api, const std::string &clientName, unsigned int queueSizeLimit ) -{ - delete rtapi_; - rtapi_ = 0; - -#if defined(__UNIX_JACK__) - if ( api == UNIX_JACK ) - rtapi_ = new MidiInJack( clientName, queueSizeLimit ); -#endif -#if defined(__LINUX_ALSA__) - if ( api == LINUX_ALSA ) - rtapi_ = new MidiInAlsa( clientName, queueSizeLimit ); -#endif -#if defined(__WINDOWS_MM__) - if ( api == WINDOWS_MM ) - rtapi_ = new MidiInWinMM( clientName, queueSizeLimit ); -#endif -#if defined(__MACOSX_CORE__) - if ( api == MACOSX_CORE ) - rtapi_ = new MidiInCore( clientName, queueSizeLimit ); -#endif -#if defined(__RTMIDI_DUMMY__) - if ( api == RTMIDI_DUMMY ) - rtapi_ = new MidiInDummy( clientName, queueSizeLimit ); -#endif -} - -RTMIDI_DLL_PUBLIC RtMidiIn :: RtMidiIn( RtMidi::Api api, const std::string &clientName, unsigned int queueSizeLimit ) - : RtMidi() -{ - if ( api != UNSPECIFIED ) { - // Attempt to open the specified API. - openMidiApi( api, clientName, queueSizeLimit ); - if ( rtapi_ ) return; - - // No compiled support for specified API value. Issue a warning - // and continue as if no API was specified. - std::cerr << "\nRtMidiIn: no compiled support for specified API argument!\n\n" << std::endl; - } - - // Iterate through the compiled APIs and return as soon as we find - // one with at least one port or we reach the end of the list. - std::vector< RtMidi::Api > apis; - getCompiledApi( apis ); - for ( unsigned int i=0; igetPortCount() ) break; - } - - if ( rtapi_ ) return; - - // It should not be possible to get here because the preprocessor - // definition __RTMIDI_DUMMY__ is automatically defined if no - // API-specific definitions are passed to the compiler. But just in - // case something weird happens, we'll throw an error. - std::string errorText = "RtMidiIn: no compiled API support found ... critical error!!"; - throw( RtMidiError( errorText, RtMidiError::UNSPECIFIED ) ); -} - -RtMidiIn :: ~RtMidiIn() throw() -{ -} - - -//*********************************************************************// -// RtMidiOut Definitions -//*********************************************************************// - -void RtMidiOut :: openMidiApi( RtMidi::Api api, const std::string &clientName ) -{ - delete rtapi_; - rtapi_ = 0; - -#if defined(__UNIX_JACK__) - if ( api == UNIX_JACK ) - rtapi_ = new MidiOutJack( clientName ); -#endif -#if defined(__LINUX_ALSA__) - if ( api == LINUX_ALSA ) - rtapi_ = new MidiOutAlsa( clientName ); -#endif -#if defined(__WINDOWS_MM__) - if ( api == WINDOWS_MM ) - rtapi_ = new MidiOutWinMM( clientName ); -#endif -#if defined(__MACOSX_CORE__) - if ( api == MACOSX_CORE ) - rtapi_ = new MidiOutCore( clientName ); -#endif -#if defined(__RTMIDI_DUMMY__) - if ( api == RTMIDI_DUMMY ) - rtapi_ = new MidiOutDummy( clientName ); -#endif -} - -RTMIDI_DLL_PUBLIC RtMidiOut :: RtMidiOut( RtMidi::Api api, const std::string &clientName) -{ - if ( api != UNSPECIFIED ) { - // Attempt to open the specified API. - openMidiApi( api, clientName ); - if ( rtapi_ ) return; - - // No compiled support for specified API value. Issue a warning - // and continue as if no API was specified. - std::cerr << "\nRtMidiOut: no compiled support for specified API argument!\n\n" << std::endl; - } - - // Iterate through the compiled APIs and return as soon as we find - // one with at least one port or we reach the end of the list. - std::vector< RtMidi::Api > apis; - getCompiledApi( apis ); - for ( unsigned int i=0; igetPortCount() ) break; - } - - if ( rtapi_ ) return; - - // It should not be possible to get here because the preprocessor - // definition __RTMIDI_DUMMY__ is automatically defined if no - // API-specific definitions are passed to the compiler. But just in - // case something weird happens, we'll thrown an error. - std::string errorText = "RtMidiOut: no compiled API support found ... critical error!!"; - throw( RtMidiError( errorText, RtMidiError::UNSPECIFIED ) ); -} - -RtMidiOut :: ~RtMidiOut() throw() -{ -} - -//*********************************************************************// -// Common MidiApi Definitions -//*********************************************************************// - -MidiApi :: MidiApi( void ) - : apiData_( 0 ), connected_( false ), errorCallback_(0), firstErrorOccurred_(false), errorCallbackUserData_(0) -{ -} - -MidiApi :: ~MidiApi( void ) -{ -} - -void MidiApi :: setErrorCallback( RtMidiErrorCallback errorCallback, void *userData = 0 ) -{ - errorCallback_ = errorCallback; - errorCallbackUserData_ = userData; -} - -void MidiApi :: error( RtMidiError::Type type, std::string errorString ) -{ - if ( errorCallback_ ) { - - if ( firstErrorOccurred_ ) - return; - - firstErrorOccurred_ = true; - const std::string errorMessage = errorString; - - errorCallback_( type, errorMessage, errorCallbackUserData_ ); - firstErrorOccurred_ = false; - return; - } - - if ( type == RtMidiError::WARNING ) { - std::cerr << '\n' << errorString << "\n\n"; - } - else if ( type == RtMidiError::DEBUG_WARNING ) { -#if defined(__RTMIDI_DEBUG__) - std::cerr << '\n' << errorString << "\n\n"; -#endif - } - else { - std::cerr << '\n' << errorString << "\n\n"; - throw RtMidiError( errorString, type ); - } -} - -//*********************************************************************// -// Common MidiInApi Definitions -//*********************************************************************// - -MidiInApi :: MidiInApi( unsigned int queueSizeLimit ) - : MidiApi() -{ - // Allocate the MIDI queue. - inputData_.queue.ringSize = queueSizeLimit; - if ( inputData_.queue.ringSize > 0 ) - inputData_.queue.ring = new MidiMessage[ inputData_.queue.ringSize ]; -} - -MidiInApi :: ~MidiInApi( void ) -{ - // Delete the MIDI queue. - if ( inputData_.queue.ringSize > 0 ) delete [] inputData_.queue.ring; -} - -void MidiInApi :: setCallback( RtMidiIn::RtMidiCallback callback, void *userData ) -{ - if ( inputData_.usingCallback ) { - errorString_ = "MidiInApi::setCallback: a callback function is already set!"; - error( RtMidiError::WARNING, errorString_ ); - return; - } - - if ( !callback ) { - errorString_ = "RtMidiIn::setCallback: callback function value is invalid!"; - error( RtMidiError::WARNING, errorString_ ); - return; - } - - inputData_.userCallback = callback; - inputData_.userData = userData; - inputData_.usingCallback = true; -} - -void MidiInApi :: cancelCallback() -{ - if ( !inputData_.usingCallback ) { - errorString_ = "RtMidiIn::cancelCallback: no callback function was set!"; - error( RtMidiError::WARNING, errorString_ ); - return; - } - - inputData_.userCallback = 0; - inputData_.userData = 0; - inputData_.usingCallback = false; -} - -void MidiInApi :: ignoreTypes( bool midiSysex, bool midiTime, bool midiSense ) -{ - inputData_.ignoreFlags = 0; - if ( midiSysex ) inputData_.ignoreFlags = 0x01; - if ( midiTime ) inputData_.ignoreFlags |= 0x02; - if ( midiSense ) inputData_.ignoreFlags |= 0x04; -} - -double MidiInApi :: getMessage( std::vector *message ) -{ - message->clear(); - - if ( inputData_.usingCallback ) { - errorString_ = "RtMidiIn::getNextMessage: a user callback is currently set for this port."; - error( RtMidiError::WARNING, errorString_ ); - return 0.0; - } - - double timeStamp; - if ( !inputData_.queue.pop( message, &timeStamp ) ) - return 0.0; - - return timeStamp; -} - -unsigned int MidiInApi::MidiQueue::size( unsigned int *__back, - unsigned int *__front ) -{ - // Access back/front members exactly once and make stack copies for - // size calculation - unsigned int _back = back, _front = front, _size; - if ( _back >= _front ) - _size = _back - _front; - else - _size = ringSize - _front + _back; - - // Return copies of back/front so no new and unsynchronized accesses - // to member variables are needed. - if ( __back ) *__back = _back; - if ( __front ) *__front = _front; - return _size; -} - -// As long as we haven't reached our queue size limit, push the message. -bool MidiInApi::MidiQueue::push( const MidiInApi::MidiMessage& msg ) -{ - // Local stack copies of front/back - unsigned int _back, _front, _size; - - // Get back/front indexes exactly once and calculate current size - _size = size( &_back, &_front ); - - if ( _size < ringSize-1 ) - { - ring[_back] = msg; - back = (back+1)%ringSize; - return true; - } - - return false; -} - -bool MidiInApi::MidiQueue::pop( std::vector *msg, double* timeStamp ) -{ - // Local stack copies of front/back - unsigned int _back, _front, _size; - - // Get back/front indexes exactly once and calculate current size - _size = size( &_back, &_front ); - - if ( _size == 0 ) - return false; - - // Copy queued message to the vector pointer argument and then "pop" it. - msg->assign( ring[_front].bytes.begin(), ring[_front].bytes.end() ); - *timeStamp = ring[_front].timeStamp; - - // Update front - front = (front+1)%ringSize; - return true; -} - -//*********************************************************************// -// Common MidiOutApi Definitions -//*********************************************************************// - -MidiOutApi :: MidiOutApi( void ) - : MidiApi() -{ -} - -MidiOutApi :: ~MidiOutApi( void ) -{ -} - -// *************************************************** // -// -// OS/API-specific methods. -// -// *************************************************** // - -#if defined(__MACOSX_CORE__) - -// The CoreMIDI API is based on the use of a callback function for -// MIDI input. We convert the system specific time stamps to delta -// time values. - -// OS-X CoreMIDI header files. -#include -#include -#include - -// A structure to hold variables related to the CoreMIDI API -// implementation. -struct CoreMidiData { - MIDIClientRef client; - MIDIPortRef port; - MIDIEndpointRef endpoint; - MIDIEndpointRef destinationId; - unsigned long long lastTime; - MIDISysexSendRequest sysexreq; -}; - -//*********************************************************************// -// API: OS-X -// Class Definitions: MidiInCore -//*********************************************************************// - -static void midiInputCallback( const MIDIPacketList *list, void *procRef, void */*srcRef*/ ) -{ - MidiInApi::RtMidiInData *data = static_cast (procRef); - CoreMidiData *apiData = static_cast (data->apiData); - - unsigned char status; - unsigned short nBytes, iByte, size; - unsigned long long time; - - bool& continueSysex = data->continueSysex; - MidiInApi::MidiMessage& message = data->message; - - const MIDIPacket *packet = &list->packet[0]; - for ( unsigned int i=0; inumPackets; ++i ) { - - // My interpretation of the CoreMIDI documentation: all message - // types, except sysex, are complete within a packet and there may - // be several of them in a single packet. Sysex messages can be - // broken across multiple packets and PacketLists but are bundled - // alone within each packet (these packets do not contain other - // message types). If sysex messages are split across multiple - // MIDIPacketLists, they must be handled by multiple calls to this - // function. - - nBytes = packet->length; - if ( nBytes == 0 ) { - packet = MIDIPacketNext( packet ); - continue; - } - - // Calculate time stamp. - if ( data->firstMessage ) { - message.timeStamp = 0.0; - data->firstMessage = false; - } - else { - time = packet->timeStamp; - if ( time == 0 ) { // this happens when receiving asynchronous sysex messages - time = AudioGetCurrentHostTime(); - } - time -= apiData->lastTime; - time = AudioConvertHostTimeToNanos( time ); - if ( !continueSysex ) - message.timeStamp = time * 0.000000001; - } - - // Track whether any non-filtered messages were found in this - // packet for timestamp calculation - bool foundNonFiltered = false; - - iByte = 0; - if ( continueSysex ) { - // We have a continuing, segmented sysex message. - if ( !( data->ignoreFlags & 0x01 ) ) { - // If we're not ignoring sysex messages, copy the entire packet. - for ( unsigned int j=0; jdata[j] ); - } - continueSysex = packet->data[nBytes-1] != 0xF7; - - if ( !( data->ignoreFlags & 0x01 ) && !continueSysex ) { - // If not a continuing sysex message, invoke the user callback function or queue the message. - if ( data->usingCallback ) { - RtMidiIn::RtMidiCallback callback = (RtMidiIn::RtMidiCallback) data->userCallback; - callback( message.timeStamp, &message.bytes, data->userData ); - } - else { - // As long as we haven't reached our queue size limit, push the message. - if ( !data->queue.push( message ) ) - std::cerr << "\nMidiInCore: message queue limit reached!!\n\n"; - } - message.bytes.clear(); - } - } - else { - while ( iByte < nBytes ) { - size = 0; - // We are expecting that the next byte in the packet is a status byte. - status = packet->data[iByte]; - if ( !(status & 0x80) ) break; - // Determine the number of bytes in the MIDI message. - if ( status < 0xC0 ) size = 3; - else if ( status < 0xE0 ) size = 2; - else if ( status < 0xF0 ) size = 3; - else if ( status == 0xF0 ) { - // A MIDI sysex - if ( data->ignoreFlags & 0x01 ) { - size = 0; - iByte = nBytes; - } - else size = nBytes - iByte; - continueSysex = packet->data[nBytes-1] != 0xF7; - } - else if ( status == 0xF1 ) { - // A MIDI time code message - if ( data->ignoreFlags & 0x02 ) { - size = 0; - iByte += 2; - } - else size = 2; - } - else if ( status == 0xF2 ) size = 3; - else if ( status == 0xF3 ) size = 2; - else if ( status == 0xF8 && ( data->ignoreFlags & 0x02 ) ) { - // A MIDI timing tick message and we're ignoring it. - size = 0; - iByte += 1; - } - else if ( status == 0xFE && ( data->ignoreFlags & 0x04 ) ) { - // A MIDI active sensing message and we're ignoring it. - size = 0; - iByte += 1; - } - else size = 1; - - // Copy the MIDI data to our vector. - if ( size ) { - foundNonFiltered = true; - message.bytes.assign( &packet->data[iByte], &packet->data[iByte+size] ); - if ( !continueSysex ) { - // If not a continuing sysex message, invoke the user callback function or queue the message. - if ( data->usingCallback ) { - RtMidiIn::RtMidiCallback callback = (RtMidiIn::RtMidiCallback) data->userCallback; - callback( message.timeStamp, &message.bytes, data->userData ); - } - else { - // As long as we haven't reached our queue size limit, push the message. - if ( !data->queue.push( message ) ) - std::cerr << "\nMidiInCore: message queue limit reached!!\n\n"; - } - message.bytes.clear(); - } - iByte += size; - } - } - } - - // Save the time of the last non-filtered message - if ( foundNonFiltered ) { - apiData->lastTime = packet->timeStamp; - if ( apiData->lastTime == 0 ) { // this happens when receiving asynchronous sysex messages - apiData->lastTime = AudioGetCurrentHostTime(); - } - } - - packet = MIDIPacketNext(packet); - } -} - -MidiInCore :: MidiInCore( const std::string &clientName, unsigned int queueSizeLimit ) - : MidiInApi( queueSizeLimit ) -{ - MidiInCore::initialize( clientName ); -} - -MidiInCore :: ~MidiInCore( void ) -{ - // Close a connection if it exists. - MidiInCore::closePort(); - - // Cleanup. - CoreMidiData *data = static_cast (apiData_); - MIDIClientDispose( data->client ); - if ( data->endpoint ) MIDIEndpointDispose( data->endpoint ); - delete data; -} - -void MidiInCore :: initialize( const std::string& clientName ) -{ - // Set up our client. - MIDIClientRef client; - CFStringRef name = CFStringCreateWithCString( NULL, clientName.c_str(), kCFStringEncodingASCII ); - OSStatus result = MIDIClientCreate(name, NULL, NULL, &client ); - if ( result != noErr ) { - std::ostringstream ost; - ost << "MidiInCore::initialize: error creating OS-X MIDI client object (" << result << ")."; - errorString_ = ost.str(); - error( RtMidiError::DRIVER_ERROR, errorString_ ); - return; - } - - // Save our api-specific connection information. - CoreMidiData *data = (CoreMidiData *) new CoreMidiData; - data->client = client; - data->endpoint = 0; - apiData_ = (void *) data; - inputData_.apiData = (void *) data; - CFRelease( name ); -} - -void MidiInCore :: openPort( unsigned int portNumber, const std::string &portName ) -{ - if ( connected_ ) { - errorString_ = "MidiInCore::openPort: a valid connection already exists!"; - error( RtMidiError::WARNING, errorString_ ); - return; - } - - CFRunLoopRunInMode( kCFRunLoopDefaultMode, 0, false ); - unsigned int nSrc = MIDIGetNumberOfSources(); - if ( nSrc < 1 ) { - errorString_ = "MidiInCore::openPort: no MIDI input sources found!"; - error( RtMidiError::NO_DEVICES_FOUND, errorString_ ); - return; - } - - if ( portNumber >= nSrc ) { - std::ostringstream ost; - ost << "MidiInCore::openPort: the 'portNumber' argument (" << portNumber << ") is invalid."; - errorString_ = ost.str(); - error( RtMidiError::INVALID_PARAMETER, errorString_ ); - return; - } - - MIDIPortRef port; - CoreMidiData *data = static_cast (apiData_); - CFStringRef portNameRef = CFStringCreateWithCString( NULL, portName.c_str(), kCFStringEncodingASCII ); - OSStatus result = MIDIInputPortCreate( data->client, - portNameRef, - midiInputCallback, (void *)&inputData_, &port ); - CFRelease( portNameRef ); - - if ( result != noErr ) { - MIDIClientDispose( data->client ); - errorString_ = "MidiInCore::openPort: error creating OS-X MIDI input port."; - error( RtMidiError::DRIVER_ERROR, errorString_ ); - return; - } - - // Get the desired input source identifier. - MIDIEndpointRef endpoint = MIDIGetSource( portNumber ); - if ( endpoint == 0 ) { - MIDIPortDispose( port ); - MIDIClientDispose( data->client ); - errorString_ = "MidiInCore::openPort: error getting MIDI input source reference."; - error( RtMidiError::DRIVER_ERROR, errorString_ ); - return; - } - - // Make the connection. - result = MIDIPortConnectSource( port, endpoint, NULL ); - if ( result != noErr ) { - MIDIPortDispose( port ); - MIDIClientDispose( data->client ); - errorString_ = "MidiInCore::openPort: error connecting OS-X MIDI input port."; - error( RtMidiError::DRIVER_ERROR, errorString_ ); - return; - } - - // Save our api-specific port information. - data->port = port; - - connected_ = true; -} - -void MidiInCore :: openVirtualPort( const std::string &portName ) -{ - CoreMidiData *data = static_cast (apiData_); - - // Create a virtual MIDI input destination. - MIDIEndpointRef endpoint; - CFStringRef portNameRef = CFStringCreateWithCString( NULL, portName.c_str(), kCFStringEncodingASCII ); - OSStatus result = MIDIDestinationCreate( data->client, - portNameRef, - midiInputCallback, (void *)&inputData_, &endpoint ); - CFRelease( portNameRef ); - - if ( result != noErr ) { - errorString_ = "MidiInCore::openVirtualPort: error creating virtual OS-X MIDI destination."; - error( RtMidiError::DRIVER_ERROR, errorString_ ); - return; - } - - // Save our api-specific connection information. - data->endpoint = endpoint; -} - -void MidiInCore :: closePort( void ) -{ - CoreMidiData *data = static_cast (apiData_); - - if ( data->endpoint ) { - MIDIEndpointDispose( data->endpoint ); - data->endpoint = 0; - } - - if ( data->port ) { - MIDIPortDispose( data->port ); - data->port = 0; - } - - connected_ = false; -} - -void MidiInCore :: setClientName ( const std::string& ) -{ - - errorString_ = "MidiInCore::setClientName: this function is not implemented for the MACOSX_CORE API!"; - error( RtMidiError::WARNING, errorString_ ); - -} - -void MidiInCore :: setPortName ( const std::string& ) -{ - - errorString_ = "MidiInCore::setPortName: this function is not implemented for the MACOSX_CORE API!"; - error( RtMidiError::WARNING, errorString_ ); - -} - -unsigned int MidiInCore :: getPortCount() -{ - CFRunLoopRunInMode( kCFRunLoopDefaultMode, 0, false ); - return MIDIGetNumberOfSources(); -} - -// This function was submitted by Douglas Casey Tucker and apparently -// derived largely from PortMidi. -CFStringRef EndpointName( MIDIEndpointRef endpoint, bool isExternal ) -{ - CFMutableStringRef result = CFStringCreateMutable( NULL, 0 ); - CFStringRef str; - - // Begin with the endpoint's name. - str = NULL; - MIDIObjectGetStringProperty( endpoint, kMIDIPropertyName, &str ); - if ( str != NULL ) { - CFStringAppend( result, str ); - CFRelease( str ); - } - - MIDIEntityRef entity = 0; - MIDIEndpointGetEntity( endpoint, &entity ); - if ( entity == 0 ) - // probably virtual - return result; - - if ( CFStringGetLength( result ) == 0 ) { - // endpoint name has zero length -- try the entity - str = NULL; - MIDIObjectGetStringProperty( entity, kMIDIPropertyName, &str ); - if ( str != NULL ) { - CFStringAppend( result, str ); - CFRelease( str ); - } - } - // now consider the device's name - MIDIDeviceRef device = 0; - MIDIEntityGetDevice( entity, &device ); - if ( device == 0 ) - return result; - - str = NULL; - MIDIObjectGetStringProperty( device, kMIDIPropertyName, &str ); - if ( CFStringGetLength( result ) == 0 ) { - CFRelease( result ); - return str; - } - if ( str != NULL ) { - // if an external device has only one entity, throw away - // the endpoint name and just use the device name - if ( isExternal && MIDIDeviceGetNumberOfEntities( device ) < 2 ) { - CFRelease( result ); - return str; - } else { - if ( CFStringGetLength( str ) == 0 ) { - CFRelease( str ); - return result; - } - // does the entity name already start with the device name? - // (some drivers do this though they shouldn't) - // if so, do not prepend - if ( CFStringCompareWithOptions( result, /* endpoint name */ - str /* device name */, - CFRangeMake(0, CFStringGetLength( str ) ), 0 ) != kCFCompareEqualTo ) { - // prepend the device name to the entity name - if ( CFStringGetLength( result ) > 0 ) - CFStringInsert( result, 0, CFSTR(" ") ); - - CFStringInsert( result, 0, str ); - } - CFRelease( str ); - } - } - return result; -} - -// This function was submitted by Douglas Casey Tucker and apparently -// derived largely from PortMidi. -static CFStringRef ConnectedEndpointName( MIDIEndpointRef endpoint ) -{ - CFMutableStringRef result = CFStringCreateMutable( NULL, 0 ); - CFStringRef str; - OSStatus err; - int i; - - // Does the endpoint have connections? - CFDataRef connections = NULL; - int nConnected = 0; - bool anyStrings = false; - err = MIDIObjectGetDataProperty( endpoint, kMIDIPropertyConnectionUniqueID, &connections ); - if ( connections != NULL ) { - // It has connections, follow them - // Concatenate the names of all connected devices - nConnected = CFDataGetLength( connections ) / sizeof(MIDIUniqueID); - if ( nConnected ) { - const SInt32 *pid = (const SInt32 *)(CFDataGetBytePtr(connections)); - for ( i=0; i= MIDIGetNumberOfSources() ) { - std::ostringstream ost; - ost << "MidiInCore::getPortName: the 'portNumber' argument (" << portNumber << ") is invalid."; - errorString_ = ost.str(); - error( RtMidiError::WARNING, errorString_ ); - return stringName; - } - - portRef = MIDIGetSource( portNumber ); - nameRef = ConnectedEndpointName( portRef ); - CFStringGetCString( nameRef, name, sizeof(name), kCFStringEncodingUTF8 ); - CFRelease( nameRef ); - - return stringName = name; -} - -//*********************************************************************// -// API: OS-X -// Class Definitions: MidiOutCore -//*********************************************************************// - -MidiOutCore :: MidiOutCore( const std::string &clientName ) - : MidiOutApi() -{ - MidiOutCore::initialize( clientName ); -} - -MidiOutCore :: ~MidiOutCore( void ) -{ - // Close a connection if it exists. - MidiOutCore::closePort(); - - // Cleanup. - CoreMidiData *data = static_cast (apiData_); - MIDIClientDispose( data->client ); - if ( data->endpoint ) MIDIEndpointDispose( data->endpoint ); - delete data; -} - -void MidiOutCore :: initialize( const std::string& clientName ) -{ - // Set up our client. - MIDIClientRef client; - CFStringRef name = CFStringCreateWithCString( NULL, clientName.c_str(), kCFStringEncodingASCII ); - OSStatus result = MIDIClientCreate(name, NULL, NULL, &client ); - if ( result != noErr ) { - std::ostringstream ost; - ost << "MidiInCore::initialize: error creating OS-X MIDI client object (" << result << ")."; - errorString_ = ost.str(); - error( RtMidiError::DRIVER_ERROR, errorString_ ); - return; - } - - // Save our api-specific connection information. - CoreMidiData *data = (CoreMidiData *) new CoreMidiData; - data->client = client; - data->endpoint = 0; - apiData_ = (void *) data; - CFRelease( name ); -} - -unsigned int MidiOutCore :: getPortCount() -{ - CFRunLoopRunInMode( kCFRunLoopDefaultMode, 0, false ); - return MIDIGetNumberOfDestinations(); -} - -std::string MidiOutCore :: getPortName( unsigned int portNumber ) -{ - CFStringRef nameRef; - MIDIEndpointRef portRef; - char name[128]; - - std::string stringName; - CFRunLoopRunInMode( kCFRunLoopDefaultMode, 0, false ); - if ( portNumber >= MIDIGetNumberOfDestinations() ) { - std::ostringstream ost; - ost << "MidiOutCore::getPortName: the 'portNumber' argument (" << portNumber << ") is invalid."; - errorString_ = ost.str(); - error( RtMidiError::WARNING, errorString_ ); - return stringName; - } - - portRef = MIDIGetDestination( portNumber ); - nameRef = ConnectedEndpointName(portRef); - CFStringGetCString( nameRef, name, sizeof(name), kCFStringEncodingUTF8 ); - CFRelease( nameRef ); - - return stringName = name; -} - -void MidiOutCore :: openPort( unsigned int portNumber, const std::string &portName ) -{ - if ( connected_ ) { - errorString_ = "MidiOutCore::openPort: a valid connection already exists!"; - error( RtMidiError::WARNING, errorString_ ); - return; - } - - CFRunLoopRunInMode( kCFRunLoopDefaultMode, 0, false ); - unsigned int nDest = MIDIGetNumberOfDestinations(); - if (nDest < 1) { - errorString_ = "MidiOutCore::openPort: no MIDI output destinations found!"; - error( RtMidiError::NO_DEVICES_FOUND, errorString_ ); - return; - } - - if ( portNumber >= nDest ) { - std::ostringstream ost; - ost << "MidiOutCore::openPort: the 'portNumber' argument (" << portNumber << ") is invalid."; - errorString_ = ost.str(); - error( RtMidiError::INVALID_PARAMETER, errorString_ ); - return; - } - - MIDIPortRef port; - CoreMidiData *data = static_cast (apiData_); - CFStringRef portNameRef = CFStringCreateWithCString( NULL, portName.c_str(), kCFStringEncodingASCII ); - OSStatus result = MIDIOutputPortCreate( data->client, portNameRef, &port ); - CFRelease( portNameRef ); - if ( result != noErr ) { - MIDIClientDispose( data->client ); - errorString_ = "MidiOutCore::openPort: error creating OS-X MIDI output port."; - error( RtMidiError::DRIVER_ERROR, errorString_ ); - return; - } - - // Get the desired output port identifier. - MIDIEndpointRef destination = MIDIGetDestination( portNumber ); - if ( destination == 0 ) { - MIDIPortDispose( port ); - MIDIClientDispose( data->client ); - errorString_ = "MidiOutCore::openPort: error getting MIDI output destination reference."; - error( RtMidiError::DRIVER_ERROR, errorString_ ); - return; - } - - // Save our api-specific connection information. - data->port = port; - data->destinationId = destination; - connected_ = true; -} - -void MidiOutCore :: closePort( void ) -{ - CoreMidiData *data = static_cast (apiData_); - - if ( data->endpoint ) { - MIDIEndpointDispose( data->endpoint ); - data->endpoint = 0; - } - - if ( data->port ) { - MIDIPortDispose( data->port ); - data->port = 0; - } - - connected_ = false; -} - -void MidiOutCore :: setClientName ( const std::string& ) -{ - - errorString_ = "MidiOutCore::setClientName: this function is not implemented for the MACOSX_CORE API!"; - error( RtMidiError::WARNING, errorString_ ); - -} - -void MidiOutCore :: setPortName ( const std::string& ) -{ - - errorString_ = "MidiOutCore::setPortName: this function is not implemented for the MACOSX_CORE API!"; - error( RtMidiError::WARNING, errorString_ ); - -} - -void MidiOutCore :: openVirtualPort( const std::string &portName ) -{ - CoreMidiData *data = static_cast (apiData_); - - if ( data->endpoint ) { - errorString_ = "MidiOutCore::openVirtualPort: a virtual output port already exists!"; - error( RtMidiError::WARNING, errorString_ ); - return; - } - - // Create a virtual MIDI output source. - MIDIEndpointRef endpoint; - CFStringRef portNameRef = CFStringCreateWithCString( NULL, portName.c_str(), kCFStringEncodingASCII ); - OSStatus result = MIDISourceCreate( data->client, portNameRef, &endpoint ); - CFRelease( portNameRef ); - - if ( result != noErr ) { - errorString_ = "MidiOutCore::initialize: error creating OS-X virtual MIDI source."; - error( RtMidiError::DRIVER_ERROR, errorString_ ); - return; - } - - // Save our api-specific connection information. - data->endpoint = endpoint; -} - -void MidiOutCore :: sendMessage( const unsigned char *message, size_t size ) -{ - // We use the MIDISendSysex() function to asynchronously send sysex - // messages. Otherwise, we use a single CoreMidi MIDIPacket. - unsigned int nBytes = static_cast (size); - if ( nBytes == 0 ) { - errorString_ = "MidiOutCore::sendMessage: no data in message argument!"; - error( RtMidiError::WARNING, errorString_ ); - return; - } - - MIDITimeStamp timeStamp = AudioGetCurrentHostTime(); - CoreMidiData *data = static_cast (apiData_); - OSStatus result; - - if ( message[0] != 0xF0 && nBytes > 3 ) { - errorString_ = "MidiOutCore::sendMessage: message format problem ... not sysex but > 3 bytes?"; - error( RtMidiError::WARNING, errorString_ ); - return; - } - - Byte buffer[nBytes+(sizeof( MIDIPacketList ))]; - ByteCount listSize = sizeof( buffer ); - MIDIPacketList *packetList = (MIDIPacketList*)buffer; - MIDIPacket *packet = MIDIPacketListInit( packetList ); - - ByteCount remainingBytes = nBytes; - while ( remainingBytes && packet ) { - ByteCount bytesForPacket = remainingBytes > 65535 ? 65535 : remainingBytes; // 65535 = maximum size of a MIDIPacket - const Byte* dataStartPtr = (const Byte *) &message[nBytes - remainingBytes]; - packet = MIDIPacketListAdd( packetList, listSize, packet, timeStamp, bytesForPacket, dataStartPtr ); - remainingBytes -= bytesForPacket; - } - - if ( !packet ) { - errorString_ = "MidiOutCore::sendMessage: could not allocate packet list"; - error( RtMidiError::DRIVER_ERROR, errorString_ ); - return; - } - - // Send to any destinations that may have connected to us. - if ( data->endpoint ) { - result = MIDIReceived( data->endpoint, packetList ); - if ( result != noErr ) { - errorString_ = "MidiOutCore::sendMessage: error sending MIDI to virtual destinations."; - error( RtMidiError::WARNING, errorString_ ); - } - } - - // And send to an explicit destination port if we're connected. - if ( connected_ ) { - result = MIDISend( data->port, data->destinationId, packetList ); - if ( result != noErr ) { - errorString_ = "MidiOutCore::sendMessage: error sending MIDI message to port."; - error( RtMidiError::WARNING, errorString_ ); - } - } -} - -#endif // __MACOSX_CORE__ - - -//*********************************************************************// -// API: LINUX ALSA SEQUENCER -//*********************************************************************// - -// API information found at: -// - http://www.alsa-project.org/documentation.php#Library - -#if defined(__LINUX_ALSA__) - -// The ALSA Sequencer API is based on the use of a callback function for -// MIDI input. -// -// Thanks to Pedro Lopez-Cabanillas for help with the ALSA sequencer -// time stamps and other assorted fixes!!! - -// If you don't need timestamping for incoming MIDI events, define the -// preprocessor definition AVOID_TIMESTAMPING to save resources -// associated with the ALSA sequencer queues. - -#include -#include - -// ALSA header file. -#include - -// A structure to hold variables related to the ALSA API -// implementation. -struct AlsaMidiData { - snd_seq_t *seq; - unsigned int portNum; - int vport; - snd_seq_port_subscribe_t *subscription; - snd_midi_event_t *coder; - unsigned int bufferSize; - unsigned char *buffer; - pthread_t thread; - pthread_t dummy_thread_id; - snd_seq_real_time_t lastTime; - int queue_id; // an input queue is needed to get timestamped events - int trigger_fds[2]; -}; - -#define PORT_TYPE( pinfo, bits ) ((snd_seq_port_info_get_capability(pinfo) & (bits)) == (bits)) - -//*********************************************************************// -// API: LINUX ALSA -// Class Definitions: MidiInAlsa -//*********************************************************************// - -static void *alsaMidiHandler( void *ptr ) -{ - MidiInApi::RtMidiInData *data = static_cast (ptr); - AlsaMidiData *apiData = static_cast (data->apiData); - - long nBytes; - double time; - bool continueSysex = false; - bool doDecode = false; - MidiInApi::MidiMessage message; - int poll_fd_count; - struct pollfd *poll_fds; - - snd_seq_event_t *ev; - int result; - apiData->bufferSize = 32; - result = snd_midi_event_new( 0, &apiData->coder ); - if ( result < 0 ) { - data->doInput = false; - std::cerr << "\nMidiInAlsa::alsaMidiHandler: error initializing MIDI event parser!\n\n"; - return 0; - } - unsigned char *buffer = (unsigned char *) malloc( apiData->bufferSize ); - if ( buffer == NULL ) { - data->doInput = false; - snd_midi_event_free( apiData->coder ); - apiData->coder = 0; - std::cerr << "\nMidiInAlsa::alsaMidiHandler: error initializing buffer memory!\n\n"; - return 0; - } - snd_midi_event_init( apiData->coder ); - snd_midi_event_no_status( apiData->coder, 1 ); // suppress running status messages - - poll_fd_count = snd_seq_poll_descriptors_count( apiData->seq, POLLIN ) + 1; - poll_fds = (struct pollfd*)alloca( poll_fd_count * sizeof( struct pollfd )); - snd_seq_poll_descriptors( apiData->seq, poll_fds + 1, poll_fd_count - 1, POLLIN ); - poll_fds[0].fd = apiData->trigger_fds[0]; - poll_fds[0].events = POLLIN; - - while ( data->doInput ) { - - if ( snd_seq_event_input_pending( apiData->seq, 1 ) == 0 ) { - // No data pending - if ( poll( poll_fds, poll_fd_count, -1) >= 0 ) { - if ( poll_fds[0].revents & POLLIN ) { - bool dummy; - int res = read( poll_fds[0].fd, &dummy, sizeof(dummy) ); - (void) res; - } - } - continue; - } - - // If here, there should be data. - result = snd_seq_event_input( apiData->seq, &ev ); - if ( result == -ENOSPC ) { - std::cerr << "\nMidiInAlsa::alsaMidiHandler: MIDI input buffer overrun!\n\n"; - continue; - } - else if ( result <= 0 ) { - std::cerr << "\nMidiInAlsa::alsaMidiHandler: unknown MIDI input error!\n"; - perror("System reports"); - continue; - } - - // This is a bit weird, but we now have to decode an ALSA MIDI - // event (back) into MIDI bytes. We'll ignore non-MIDI types. - if ( !continueSysex ) message.bytes.clear(); - - doDecode = false; - switch ( ev->type ) { - - case SND_SEQ_EVENT_PORT_SUBSCRIBED: -#if defined(__RTMIDI_DEBUG__) - std::cout << "MidiInAlsa::alsaMidiHandler: port connection made!\n"; -#endif - break; - - case SND_SEQ_EVENT_PORT_UNSUBSCRIBED: -#if defined(__RTMIDI_DEBUG__) - std::cerr << "MidiInAlsa::alsaMidiHandler: port connection has closed!\n"; - std::cout << "sender = " << (int) ev->data.connect.sender.client << ":" - << (int) ev->data.connect.sender.port - << ", dest = " << (int) ev->data.connect.dest.client << ":" - << (int) ev->data.connect.dest.port - << std::endl; -#endif - break; - - case SND_SEQ_EVENT_QFRAME: // MIDI time code - if ( !( data->ignoreFlags & 0x02 ) ) doDecode = true; - break; - - case SND_SEQ_EVENT_TICK: // 0xF9 ... MIDI timing tick - if ( !( data->ignoreFlags & 0x02 ) ) doDecode = true; - break; - - case SND_SEQ_EVENT_CLOCK: // 0xF8 ... MIDI timing (clock) tick - if ( !( data->ignoreFlags & 0x02 ) ) doDecode = true; - break; - - case SND_SEQ_EVENT_SENSING: // Active sensing - if ( !( data->ignoreFlags & 0x04 ) ) doDecode = true; - break; - - case SND_SEQ_EVENT_SYSEX: - if ( (data->ignoreFlags & 0x01) ) break; - if ( ev->data.ext.len > apiData->bufferSize ) { - apiData->bufferSize = ev->data.ext.len; - free( buffer ); - buffer = (unsigned char *) malloc( apiData->bufferSize ); - if ( buffer == NULL ) { - data->doInput = false; - std::cerr << "\nMidiInAlsa::alsaMidiHandler: error resizing buffer memory!\n\n"; - break; - } - } - doDecode = true; - break; - - default: - doDecode = true; - } - - if ( doDecode ) { - - nBytes = snd_midi_event_decode( apiData->coder, buffer, apiData->bufferSize, ev ); - if ( nBytes > 0 ) { - // The ALSA sequencer has a maximum buffer size for MIDI sysex - // events of 256 bytes. If a device sends sysex messages larger - // than this, they are segmented into 256 byte chunks. So, - // we'll watch for this and concatenate sysex chunks into a - // single sysex message if necessary. - if ( !continueSysex ) - message.bytes.assign( buffer, &buffer[nBytes] ); - else - message.bytes.insert( message.bytes.end(), buffer, &buffer[nBytes] ); - - continueSysex = ( ( ev->type == SND_SEQ_EVENT_SYSEX ) && ( message.bytes.back() != 0xF7 ) ); - if ( !continueSysex ) { - - // Calculate the time stamp: - message.timeStamp = 0.0; - - // Method 1: Use the system time. - //(void)gettimeofday(&tv, (struct timezone *)NULL); - //time = (tv.tv_sec * 1000000) + tv.tv_usec; - - // Method 2: Use the ALSA sequencer event time data. - // (thanks to Pedro Lopez-Cabanillas!). - - // Using method from: - // https://www.gnu.org/software/libc/manual/html_node/Elapsed-Time.html - - // Perform the carry for the later subtraction by updating y. - // Temp var y is timespec because computation requires signed types, - // while snd_seq_real_time_t has unsigned types. - snd_seq_real_time_t &x( ev->time.time ); - struct timespec y; - y.tv_nsec = apiData->lastTime.tv_nsec; - y.tv_sec = apiData->lastTime.tv_sec; - if ( x.tv_nsec < y.tv_nsec ) { - int nsec = (y.tv_nsec - (int)x.tv_nsec) / 1000000000 + 1; - y.tv_nsec -= 1000000000 * nsec; - y.tv_sec += nsec; - } - if ( x.tv_nsec - y.tv_nsec > 1000000000 ) { - int nsec = ((int)x.tv_nsec - y.tv_nsec) / 1000000000; - y.tv_nsec += 1000000000 * nsec; - y.tv_sec -= nsec; - } - - // Compute the time difference. - time = (int)x.tv_sec - y.tv_sec + ((int)x.tv_nsec - y.tv_nsec)*1e-9; - - apiData->lastTime = ev->time.time; - - if ( data->firstMessage == true ) - data->firstMessage = false; - else - message.timeStamp = time; - } - else { -#if defined(__RTMIDI_DEBUG__) - std::cerr << "\nMidiInAlsa::alsaMidiHandler: event parsing error or not a MIDI event!\n\n"; -#endif - } - } - } - - snd_seq_free_event( ev ); - if ( message.bytes.size() == 0 || continueSysex ) continue; - - if ( data->usingCallback ) { - RtMidiIn::RtMidiCallback callback = (RtMidiIn::RtMidiCallback) data->userCallback; - callback( message.timeStamp, &message.bytes, data->userData ); - } - else { - // As long as we haven't reached our queue size limit, push the message. - if ( !data->queue.push( message ) ) - std::cerr << "\nMidiInAlsa: message queue limit reached!!\n\n"; - } - } - - if ( buffer ) free( buffer ); - snd_midi_event_free( apiData->coder ); - apiData->coder = 0; - apiData->thread = apiData->dummy_thread_id; - return 0; -} - -MidiInAlsa :: MidiInAlsa( const std::string &clientName, unsigned int queueSizeLimit ) - : MidiInApi( queueSizeLimit ) -{ - MidiInAlsa::initialize( clientName ); -} - -MidiInAlsa :: ~MidiInAlsa() -{ - // Close a connection if it exists. - MidiInAlsa::closePort(); - - // Shutdown the input thread. - AlsaMidiData *data = static_cast (apiData_); - if ( inputData_.doInput ) { - inputData_.doInput = false; - int res = write( data->trigger_fds[1], &inputData_.doInput, sizeof( inputData_.doInput ) ); - (void) res; - if ( !pthread_equal(data->thread, data->dummy_thread_id) ) - pthread_join( data->thread, NULL ); - } - - // Cleanup. - close ( data->trigger_fds[0] ); - close ( data->trigger_fds[1] ); - if ( data->vport >= 0 ) snd_seq_delete_port( data->seq, data->vport ); -#ifndef AVOID_TIMESTAMPING - snd_seq_free_queue( data->seq, data->queue_id ); -#endif - snd_seq_close( data->seq ); - delete data; -} - -void MidiInAlsa :: initialize( const std::string& clientName ) -{ - // Set up the ALSA sequencer client. - snd_seq_t *seq; - int result = snd_seq_open( &seq, "default", SND_SEQ_OPEN_DUPLEX, SND_SEQ_NONBLOCK ); - if ( result < 0 ) { - errorString_ = "MidiInAlsa::initialize: error creating ALSA sequencer client object."; - error( RtMidiError::DRIVER_ERROR, errorString_ ); - return; - } - - // Set client name. - snd_seq_set_client_name( seq, clientName.c_str() ); - - // Save our api-specific connection information. - AlsaMidiData *data = (AlsaMidiData *) new AlsaMidiData; - data->seq = seq; - data->portNum = -1; - data->vport = -1; - data->subscription = 0; - data->dummy_thread_id = pthread_self(); - data->thread = data->dummy_thread_id; - data->trigger_fds[0] = -1; - data->trigger_fds[1] = -1; - apiData_ = (void *) data; - inputData_.apiData = (void *) data; - - if ( pipe(data->trigger_fds) == -1 ) { - errorString_ = "MidiInAlsa::initialize: error creating pipe objects."; - error( RtMidiError::DRIVER_ERROR, errorString_ ); - return; - } - - // Create the input queue -#ifndef AVOID_TIMESTAMPING - data->queue_id = snd_seq_alloc_named_queue( seq, "RtMidi Queue" ); - // Set arbitrary tempo (mm=100) and resolution (240) - snd_seq_queue_tempo_t *qtempo; - snd_seq_queue_tempo_alloca( &qtempo ); - snd_seq_queue_tempo_set_tempo( qtempo, 600000 ); - snd_seq_queue_tempo_set_ppq( qtempo, 240 ); - snd_seq_set_queue_tempo( data->seq, data->queue_id, qtempo ); - snd_seq_drain_output( data->seq ); -#endif -} - -// This function is used to count or get the pinfo structure for a given port number. -unsigned int portInfo( snd_seq_t *seq, snd_seq_port_info_t *pinfo, unsigned int type, int portNumber ) -{ - snd_seq_client_info_t *cinfo; - int client; - int count = 0; - snd_seq_client_info_alloca( &cinfo ); - - snd_seq_client_info_set_client( cinfo, -1 ); - while ( snd_seq_query_next_client( seq, cinfo ) >= 0 ) { - client = snd_seq_client_info_get_client( cinfo ); - if ( client == 0 ) continue; - // Reset query info - snd_seq_port_info_set_client( pinfo, client ); - snd_seq_port_info_set_port( pinfo, -1 ); - while ( snd_seq_query_next_port( seq, pinfo ) >= 0 ) { - unsigned int atyp = snd_seq_port_info_get_type( pinfo ); - if ( ( ( atyp & SND_SEQ_PORT_TYPE_MIDI_GENERIC ) == 0 ) && - ( ( atyp & SND_SEQ_PORT_TYPE_SYNTH ) == 0 ) && - ( ( atyp & SND_SEQ_PORT_TYPE_APPLICATION ) == 0 ) ) continue; - - unsigned int caps = snd_seq_port_info_get_capability( pinfo ); - if ( ( caps & type ) != type ) continue; - if ( count == portNumber ) return 1; - ++count; - } - } - - // If a negative portNumber was used, return the port count. - if ( portNumber < 0 ) return count; - return 0; -} - -unsigned int MidiInAlsa :: getPortCount() -{ - snd_seq_port_info_t *pinfo; - snd_seq_port_info_alloca( &pinfo ); - - AlsaMidiData *data = static_cast (apiData_); - return portInfo( data->seq, pinfo, SND_SEQ_PORT_CAP_READ|SND_SEQ_PORT_CAP_SUBS_READ, -1 ); -} - -std::string MidiInAlsa :: getPortName( unsigned int portNumber ) -{ - snd_seq_client_info_t *cinfo; - snd_seq_port_info_t *pinfo; - snd_seq_client_info_alloca( &cinfo ); - snd_seq_port_info_alloca( &pinfo ); - - std::string stringName; - AlsaMidiData *data = static_cast (apiData_); - if ( portInfo( data->seq, pinfo, SND_SEQ_PORT_CAP_READ|SND_SEQ_PORT_CAP_SUBS_READ, (int) portNumber ) ) { - int cnum = snd_seq_port_info_get_client( pinfo ); - snd_seq_get_any_client_info( data->seq, cnum, cinfo ); - std::ostringstream os; - os << snd_seq_client_info_get_name( cinfo ); - os << ":"; - os << snd_seq_port_info_get_name( pinfo ); - os << " "; // These lines added to make sure devices are listed - os << snd_seq_port_info_get_client( pinfo ); // with full portnames added to ensure individual device names - os << ":"; - os << snd_seq_port_info_get_port( pinfo ); - stringName = os.str(); - return stringName; - } - - // If we get here, we didn't find a match. - errorString_ = "MidiInAlsa::getPortName: error looking for port name!"; - error( RtMidiError::WARNING, errorString_ ); - return stringName; -} - -void MidiInAlsa :: openPort( unsigned int portNumber, const std::string &portName ) -{ - if ( connected_ ) { - errorString_ = "MidiInAlsa::openPort: a valid connection already exists!"; - error( RtMidiError::WARNING, errorString_ ); - return; - } - - unsigned int nSrc = this->getPortCount(); - if ( nSrc < 1 ) { - errorString_ = "MidiInAlsa::openPort: no MIDI input sources found!"; - error( RtMidiError::NO_DEVICES_FOUND, errorString_ ); - return; - } - - snd_seq_port_info_t *src_pinfo; - snd_seq_port_info_alloca( &src_pinfo ); - AlsaMidiData *data = static_cast (apiData_); - if ( portInfo( data->seq, src_pinfo, SND_SEQ_PORT_CAP_READ|SND_SEQ_PORT_CAP_SUBS_READ, (int) portNumber ) == 0 ) { - std::ostringstream ost; - ost << "MidiInAlsa::openPort: the 'portNumber' argument (" << portNumber << ") is invalid."; - errorString_ = ost.str(); - error( RtMidiError::INVALID_PARAMETER, errorString_ ); - return; - } - - snd_seq_addr_t sender, receiver; - sender.client = snd_seq_port_info_get_client( src_pinfo ); - sender.port = snd_seq_port_info_get_port( src_pinfo ); - receiver.client = snd_seq_client_id( data->seq ); - - snd_seq_port_info_t *pinfo; - snd_seq_port_info_alloca( &pinfo ); - if ( data->vport < 0 ) { - snd_seq_port_info_set_client( pinfo, 0 ); - snd_seq_port_info_set_port( pinfo, 0 ); - snd_seq_port_info_set_capability( pinfo, - SND_SEQ_PORT_CAP_WRITE | - SND_SEQ_PORT_CAP_SUBS_WRITE ); - snd_seq_port_info_set_type( pinfo, - SND_SEQ_PORT_TYPE_MIDI_GENERIC | - SND_SEQ_PORT_TYPE_APPLICATION ); - snd_seq_port_info_set_midi_channels(pinfo, 16); -#ifndef AVOID_TIMESTAMPING - snd_seq_port_info_set_timestamping( pinfo, 1 ); - snd_seq_port_info_set_timestamp_real( pinfo, 1 ); - snd_seq_port_info_set_timestamp_queue( pinfo, data->queue_id ); -#endif - snd_seq_port_info_set_name( pinfo, portName.c_str() ); - data->vport = snd_seq_create_port( data->seq, pinfo ); - - if ( data->vport < 0 ) { - errorString_ = "MidiInAlsa::openPort: ALSA error creating input port."; - error( RtMidiError::DRIVER_ERROR, errorString_ ); - return; - } - data->vport = snd_seq_port_info_get_port( pinfo ); - } - - receiver.port = data->vport; - - if ( !data->subscription ) { - // Make subscription - if ( snd_seq_port_subscribe_malloc( &data->subscription ) < 0 ) { - errorString_ = "MidiInAlsa::openPort: ALSA error allocation port subscription."; - error( RtMidiError::DRIVER_ERROR, errorString_ ); - return; - } - snd_seq_port_subscribe_set_sender( data->subscription, &sender ); - snd_seq_port_subscribe_set_dest( data->subscription, &receiver ); - if ( snd_seq_subscribe_port( data->seq, data->subscription ) ) { - snd_seq_port_subscribe_free( data->subscription ); - data->subscription = 0; - errorString_ = "MidiInAlsa::openPort: ALSA error making port connection."; - error( RtMidiError::DRIVER_ERROR, errorString_ ); - return; - } - } - - if ( inputData_.doInput == false ) { - // Start the input queue -#ifndef AVOID_TIMESTAMPING - snd_seq_start_queue( data->seq, data->queue_id, NULL ); - snd_seq_drain_output( data->seq ); -#endif - // Start our MIDI input thread. - pthread_attr_t attr; - pthread_attr_init( &attr ); - pthread_attr_setdetachstate( &attr, PTHREAD_CREATE_JOINABLE ); - pthread_attr_setschedpolicy( &attr, SCHED_OTHER ); - - inputData_.doInput = true; - int err = pthread_create( &data->thread, &attr, alsaMidiHandler, &inputData_ ); - pthread_attr_destroy( &attr ); - if ( err ) { - snd_seq_unsubscribe_port( data->seq, data->subscription ); - snd_seq_port_subscribe_free( data->subscription ); - data->subscription = 0; - inputData_.doInput = false; - errorString_ = "MidiInAlsa::openPort: error starting MIDI input thread!"; - error( RtMidiError::THREAD_ERROR, errorString_ ); - return; - } - } - - connected_ = true; -} - -void MidiInAlsa :: openVirtualPort( const std::string &portName ) -{ - AlsaMidiData *data = static_cast (apiData_); - if ( data->vport < 0 ) { - snd_seq_port_info_t *pinfo; - snd_seq_port_info_alloca( &pinfo ); - snd_seq_port_info_set_capability( pinfo, - SND_SEQ_PORT_CAP_WRITE | - SND_SEQ_PORT_CAP_SUBS_WRITE ); - snd_seq_port_info_set_type( pinfo, - SND_SEQ_PORT_TYPE_MIDI_GENERIC | - SND_SEQ_PORT_TYPE_APPLICATION ); - snd_seq_port_info_set_midi_channels( pinfo, 16 ); -#ifndef AVOID_TIMESTAMPING - snd_seq_port_info_set_timestamping( pinfo, 1 ); - snd_seq_port_info_set_timestamp_real( pinfo, 1 ); - snd_seq_port_info_set_timestamp_queue( pinfo, data->queue_id ); -#endif - snd_seq_port_info_set_name( pinfo, portName.c_str() ); - data->vport = snd_seq_create_port( data->seq, pinfo ); - - if ( data->vport < 0 ) { - errorString_ = "MidiInAlsa::openVirtualPort: ALSA error creating virtual port."; - error( RtMidiError::DRIVER_ERROR, errorString_ ); - return; - } - data->vport = snd_seq_port_info_get_port( pinfo ); - } - - if ( inputData_.doInput == false ) { - // Wait for old thread to stop, if still running - if ( !pthread_equal( data->thread, data->dummy_thread_id ) ) - pthread_join( data->thread, NULL ); - - // Start the input queue -#ifndef AVOID_TIMESTAMPING - snd_seq_start_queue( data->seq, data->queue_id, NULL ); - snd_seq_drain_output( data->seq ); -#endif - // Start our MIDI input thread. - pthread_attr_t attr; - pthread_attr_init( &attr ); - pthread_attr_setdetachstate( &attr, PTHREAD_CREATE_JOINABLE ); - pthread_attr_setschedpolicy( &attr, SCHED_OTHER ); - - inputData_.doInput = true; - int err = pthread_create( &data->thread, &attr, alsaMidiHandler, &inputData_ ); - pthread_attr_destroy( &attr ); - if ( err ) { - if ( data->subscription ) { - snd_seq_unsubscribe_port( data->seq, data->subscription ); - snd_seq_port_subscribe_free( data->subscription ); - data->subscription = 0; - } - inputData_.doInput = false; - errorString_ = "MidiInAlsa::openPort: error starting MIDI input thread!"; - error( RtMidiError::THREAD_ERROR, errorString_ ); - return; - } - } -} - -void MidiInAlsa :: closePort( void ) -{ - AlsaMidiData *data = static_cast (apiData_); - - if ( connected_ ) { - if ( data->subscription ) { - snd_seq_unsubscribe_port( data->seq, data->subscription ); - snd_seq_port_subscribe_free( data->subscription ); - data->subscription = 0; - } - // Stop the input queue -#ifndef AVOID_TIMESTAMPING - snd_seq_stop_queue( data->seq, data->queue_id, NULL ); - snd_seq_drain_output( data->seq ); -#endif - connected_ = false; - } - - // Stop thread to avoid triggering the callback, while the port is intended to be closed - if ( inputData_.doInput ) { - inputData_.doInput = false; - int res = write( data->trigger_fds[1], &inputData_.doInput, sizeof( inputData_.doInput ) ); - (void) res; - if ( !pthread_equal( data->thread, data->dummy_thread_id ) ) - pthread_join( data->thread, NULL ); - } -} - -void MidiInAlsa :: setClientName( const std::string &clientName ) -{ - - AlsaMidiData *data = static_cast ( apiData_ ); - snd_seq_set_client_name( data->seq, clientName.c_str() ); - -} - -void MidiInAlsa :: setPortName( const std::string &portName ) -{ - AlsaMidiData *data = static_cast (apiData_); - snd_seq_port_info_t *pinfo; - snd_seq_port_info_alloca( &pinfo ); - snd_seq_get_port_info( data->seq, data->vport, pinfo ); - snd_seq_port_info_set_name( pinfo, portName.c_str() ); - snd_seq_set_port_info( data->seq, data->vport, pinfo ); -} - -//*********************************************************************// -// API: LINUX ALSA -// Class Definitions: MidiOutAlsa -//*********************************************************************// - -MidiOutAlsa :: MidiOutAlsa( const std::string &clientName ) : MidiOutApi() -{ - MidiOutAlsa::initialize( clientName ); -} - -MidiOutAlsa :: ~MidiOutAlsa() -{ - // Close a connection if it exists. - MidiOutAlsa::closePort(); - - // Cleanup. - AlsaMidiData *data = static_cast (apiData_); - if ( data->vport >= 0 ) snd_seq_delete_port( data->seq, data->vport ); - if ( data->coder ) snd_midi_event_free( data->coder ); - if ( data->buffer ) free( data->buffer ); - snd_seq_close( data->seq ); - delete data; -} - -void MidiOutAlsa :: initialize( const std::string& clientName ) -{ - // Set up the ALSA sequencer client. - snd_seq_t *seq; - int result1 = snd_seq_open( &seq, "default", SND_SEQ_OPEN_OUTPUT, SND_SEQ_NONBLOCK ); - if ( result1 < 0 ) { - errorString_ = "MidiOutAlsa::initialize: error creating ALSA sequencer client object."; - error( RtMidiError::DRIVER_ERROR, errorString_ ); - return; - } - - // Set client name. - snd_seq_set_client_name( seq, clientName.c_str() ); - - // Save our api-specific connection information. - AlsaMidiData *data = (AlsaMidiData *) new AlsaMidiData; - data->seq = seq; - data->portNum = -1; - data->vport = -1; - data->bufferSize = 32; - data->coder = 0; - data->buffer = 0; - int result = snd_midi_event_new( data->bufferSize, &data->coder ); - if ( result < 0 ) { - delete data; - errorString_ = "MidiOutAlsa::initialize: error initializing MIDI event parser!\n\n"; - error( RtMidiError::DRIVER_ERROR, errorString_ ); - return; - } - data->buffer = (unsigned char *) malloc( data->bufferSize ); - if ( data->buffer == NULL ) { - delete data; - errorString_ = "MidiOutAlsa::initialize: error allocating buffer memory!\n\n"; - error( RtMidiError::MEMORY_ERROR, errorString_ ); - return; - } - snd_midi_event_init( data->coder ); - apiData_ = (void *) data; -} - -unsigned int MidiOutAlsa :: getPortCount() -{ - snd_seq_port_info_t *pinfo; - snd_seq_port_info_alloca( &pinfo ); - - AlsaMidiData *data = static_cast (apiData_); - return portInfo( data->seq, pinfo, SND_SEQ_PORT_CAP_WRITE|SND_SEQ_PORT_CAP_SUBS_WRITE, -1 ); -} - -std::string MidiOutAlsa :: getPortName( unsigned int portNumber ) -{ - snd_seq_client_info_t *cinfo; - snd_seq_port_info_t *pinfo; - snd_seq_client_info_alloca( &cinfo ); - snd_seq_port_info_alloca( &pinfo ); - - std::string stringName; - AlsaMidiData *data = static_cast (apiData_); - if ( portInfo( data->seq, pinfo, SND_SEQ_PORT_CAP_WRITE|SND_SEQ_PORT_CAP_SUBS_WRITE, (int) portNumber ) ) { - int cnum = snd_seq_port_info_get_client( pinfo ); - snd_seq_get_any_client_info( data->seq, cnum, cinfo ); - std::ostringstream os; - os << snd_seq_client_info_get_name( cinfo ); - os << ":"; - os << snd_seq_port_info_get_name( pinfo ); - os << " "; // These lines added to make sure devices are listed - os << snd_seq_port_info_get_client( pinfo ); // with full portnames added to ensure individual device names - os << ":"; - os << snd_seq_port_info_get_port( pinfo ); - stringName = os.str(); - return stringName; - } - - // If we get here, we didn't find a match. - errorString_ = "MidiOutAlsa::getPortName: error looking for port name!"; - error( RtMidiError::WARNING, errorString_ ); - return stringName; -} - -void MidiOutAlsa :: openPort( unsigned int portNumber, const std::string &portName ) -{ - if ( connected_ ) { - errorString_ = "MidiOutAlsa::openPort: a valid connection already exists!"; - error( RtMidiError::WARNING, errorString_ ); - return; - } - - unsigned int nSrc = this->getPortCount(); - if ( nSrc < 1 ) { - errorString_ = "MidiOutAlsa::openPort: no MIDI output sources found!"; - error( RtMidiError::NO_DEVICES_FOUND, errorString_ ); - return; - } - - snd_seq_port_info_t *pinfo; - snd_seq_port_info_alloca( &pinfo ); - AlsaMidiData *data = static_cast (apiData_); - if ( portInfo( data->seq, pinfo, SND_SEQ_PORT_CAP_WRITE|SND_SEQ_PORT_CAP_SUBS_WRITE, (int) portNumber ) == 0 ) { - std::ostringstream ost; - ost << "MidiOutAlsa::openPort: the 'portNumber' argument (" << portNumber << ") is invalid."; - errorString_ = ost.str(); - error( RtMidiError::INVALID_PARAMETER, errorString_ ); - return; - } - - snd_seq_addr_t sender, receiver; - receiver.client = snd_seq_port_info_get_client( pinfo ); - receiver.port = snd_seq_port_info_get_port( pinfo ); - sender.client = snd_seq_client_id( data->seq ); - - if ( data->vport < 0 ) { - data->vport = snd_seq_create_simple_port( data->seq, portName.c_str(), - SND_SEQ_PORT_CAP_READ|SND_SEQ_PORT_CAP_SUBS_READ, - SND_SEQ_PORT_TYPE_MIDI_GENERIC|SND_SEQ_PORT_TYPE_APPLICATION ); - if ( data->vport < 0 ) { - errorString_ = "MidiOutAlsa::openPort: ALSA error creating output port."; - error( RtMidiError::DRIVER_ERROR, errorString_ ); - return; - } - } - - sender.port = data->vport; - - // Make subscription - if ( snd_seq_port_subscribe_malloc( &data->subscription ) < 0 ) { - snd_seq_port_subscribe_free( data->subscription ); - errorString_ = "MidiOutAlsa::openPort: error allocating port subscription."; - error( RtMidiError::DRIVER_ERROR, errorString_ ); - return; - } - snd_seq_port_subscribe_set_sender( data->subscription, &sender ); - snd_seq_port_subscribe_set_dest( data->subscription, &receiver ); - snd_seq_port_subscribe_set_time_update( data->subscription, 1 ); - snd_seq_port_subscribe_set_time_real( data->subscription, 1 ); - if ( snd_seq_subscribe_port( data->seq, data->subscription ) ) { - snd_seq_port_subscribe_free( data->subscription ); - errorString_ = "MidiOutAlsa::openPort: ALSA error making port connection."; - error( RtMidiError::DRIVER_ERROR, errorString_ ); - return; - } - - connected_ = true; -} - -void MidiOutAlsa :: closePort( void ) -{ - if ( connected_ ) { - AlsaMidiData *data = static_cast (apiData_); - snd_seq_unsubscribe_port( data->seq, data->subscription ); - snd_seq_port_subscribe_free( data->subscription ); - data->subscription = 0; - connected_ = false; - } -} - -void MidiOutAlsa :: setClientName( const std::string &clientName ) -{ - - AlsaMidiData *data = static_cast ( apiData_ ); - snd_seq_set_client_name( data->seq, clientName.c_str() ); - -} - -void MidiOutAlsa :: setPortName( const std::string &portName ) -{ - AlsaMidiData *data = static_cast (apiData_); - snd_seq_port_info_t *pinfo; - snd_seq_port_info_alloca( &pinfo ); - snd_seq_get_port_info( data->seq, data->vport, pinfo ); - snd_seq_port_info_set_name( pinfo, portName.c_str() ); - snd_seq_set_port_info( data->seq, data->vport, pinfo ); -} - -void MidiOutAlsa :: openVirtualPort( const std::string &portName ) -{ - AlsaMidiData *data = static_cast (apiData_); - if ( data->vport < 0 ) { - data->vport = snd_seq_create_simple_port( data->seq, portName.c_str(), - SND_SEQ_PORT_CAP_READ|SND_SEQ_PORT_CAP_SUBS_READ, - SND_SEQ_PORT_TYPE_MIDI_GENERIC|SND_SEQ_PORT_TYPE_APPLICATION ); - - if ( data->vport < 0 ) { - errorString_ = "MidiOutAlsa::openVirtualPort: ALSA error creating virtual port."; - error( RtMidiError::DRIVER_ERROR, errorString_ ); - } - } -} - -void MidiOutAlsa :: sendMessage( const unsigned char *message, size_t size ) -{ - int result; - AlsaMidiData *data = static_cast (apiData_); - unsigned int nBytes = static_cast (size); - if ( nBytes > data->bufferSize ) { - data->bufferSize = nBytes; - result = snd_midi_event_resize_buffer( data->coder, nBytes ); - if ( result != 0 ) { - errorString_ = "MidiOutAlsa::sendMessage: ALSA error resizing MIDI event buffer."; - error( RtMidiError::DRIVER_ERROR, errorString_ ); - return; - } - free (data->buffer); - data->buffer = (unsigned char *) malloc( data->bufferSize ); - if ( data->buffer == NULL ) { - errorString_ = "MidiOutAlsa::initialize: error allocating buffer memory!\n\n"; - error( RtMidiError::MEMORY_ERROR, errorString_ ); - return; - } - } - - snd_seq_event_t ev; - snd_seq_ev_clear( &ev ); - snd_seq_ev_set_source( &ev, data->vport ); - snd_seq_ev_set_subs( &ev ); - snd_seq_ev_set_direct( &ev ); - for ( unsigned int i=0; ibuffer[i] = message[i]; - result = snd_midi_event_encode( data->coder, data->buffer, (long)nBytes, &ev ); - if ( result < (int)nBytes ) { - errorString_ = "MidiOutAlsa::sendMessage: event parsing error!"; - error( RtMidiError::WARNING, errorString_ ); - return; - } - - // Send the event. - result = snd_seq_event_output( data->seq, &ev ); - if ( result < 0 ) { - errorString_ = "MidiOutAlsa::sendMessage: error sending MIDI message to port."; - error( RtMidiError::WARNING, errorString_ ); - return; - } - snd_seq_drain_output( data->seq ); -} - -#endif // __LINUX_ALSA__ - - -//*********************************************************************// -// API: Windows Multimedia Library (MM) -//*********************************************************************// - -// API information deciphered from: -// - http://msdn.microsoft.com/library/default.asp?url=/library/en-us/multimed/htm/_win32_midi_reference.asp - -// Thanks to Jean-Baptiste Berruchon for the sysex code. - -#if defined(__WINDOWS_MM__) - -// The Windows MM API is based on the use of a callback function for -// MIDI input. We convert the system specific time stamps to delta -// time values. - -// Windows MM MIDI header files. -#include -#include - -// Convert a null-terminated wide string or ANSI-encoded string to UTF-8. -static std::string ConvertToUTF8(const TCHAR *str) -{ - std::string u8str; - const WCHAR *wstr = L""; -#if defined( UNICODE ) || defined( _UNICODE ) - wstr = str; -#else - // Convert from ANSI encoding to wide string - int wlength = MultiByteToWideChar( CP_ACP, 0, str, -1, NULL, 0 ); - std::wstring wstrtemp; - if ( wlength ) - { - wstrtemp.assign( wlength - 1, 0 ); - MultiByteToWideChar( CP_ACP, 0, str, -1, &wstrtemp[0], wlength ); - wstr = &wstrtemp[0]; - } -#endif - // Convert from wide string to UTF-8 - int length = WideCharToMultiByte( CP_UTF8, 0, wstr, -1, NULL, 0, NULL, NULL ); - if ( length ) - { - u8str.assign( length - 1, 0 ); - length = WideCharToMultiByte( CP_UTF8, 0, wstr, -1, &u8str[0], length, NULL, NULL ); - } - return u8str; -} - -#define RT_SYSEX_BUFFER_SIZE 1024 -#define RT_SYSEX_BUFFER_COUNT 4 - -// A structure to hold variables related to the CoreMIDI API -// implementation. -struct WinMidiData { - HMIDIIN inHandle; // Handle to Midi Input Device - HMIDIOUT outHandle; // Handle to Midi Output Device - DWORD lastTime; - MidiInApi::MidiMessage message; - LPMIDIHDR sysexBuffer[RT_SYSEX_BUFFER_COUNT]; - CRITICAL_SECTION _mutex; // [Patrice] see https://groups.google.com/forum/#!topic/mididev/6OUjHutMpEo -}; - -//*********************************************************************// -// API: Windows MM -// Class Definitions: MidiInWinMM -//*********************************************************************// - -static void CALLBACK midiInputCallback( HMIDIIN /*hmin*/, - UINT inputStatus, - DWORD_PTR instancePtr, - DWORD_PTR midiMessage, - DWORD timestamp ) -{ - if ( inputStatus != MIM_DATA && inputStatus != MIM_LONGDATA && inputStatus != MIM_LONGERROR ) return; - - //MidiInApi::RtMidiInData *data = static_cast (instancePtr); - MidiInApi::RtMidiInData *data = (MidiInApi::RtMidiInData *)instancePtr; - WinMidiData *apiData = static_cast (data->apiData); - - // Calculate time stamp. - if ( data->firstMessage == true ) { - apiData->message.timeStamp = 0.0; - data->firstMessage = false; - } - else apiData->message.timeStamp = (double) ( timestamp - apiData->lastTime ) * 0.001; - - if ( inputStatus == MIM_DATA ) { // Channel or system message - - // Make sure the first byte is a status byte. - unsigned char status = (unsigned char) (midiMessage & 0x000000FF); - if ( !(status & 0x80) ) return; - - // Determine the number of bytes in the MIDI message. - unsigned short nBytes = 1; - if ( status < 0xC0 ) nBytes = 3; - else if ( status < 0xE0 ) nBytes = 2; - else if ( status < 0xF0 ) nBytes = 3; - else if ( status == 0xF1 ) { - if ( data->ignoreFlags & 0x02 ) return; - else nBytes = 2; - } - else if ( status == 0xF2 ) nBytes = 3; - else if ( status == 0xF3 ) nBytes = 2; - else if ( status == 0xF8 && ( data->ignoreFlags & 0x02 ) ) { - // A MIDI timing tick message and we're ignoring it. - return; - } - else if ( status == 0xFE && ( data->ignoreFlags & 0x04 ) ) { - // A MIDI active sensing message and we're ignoring it. - return; - } - - // Copy bytes to our MIDI message. - unsigned char *ptr = (unsigned char *) &midiMessage; - for ( int i=0; imessage.bytes.push_back( *ptr++ ); - } - else { // Sysex message ( MIM_LONGDATA or MIM_LONGERROR ) - MIDIHDR *sysex = ( MIDIHDR *) midiMessage; - if ( !( data->ignoreFlags & 0x01 ) && inputStatus != MIM_LONGERROR ) { - // Sysex message and we're not ignoring it - for ( int i=0; i<(int)sysex->dwBytesRecorded; ++i ) - apiData->message.bytes.push_back( sysex->lpData[i] ); - } - - // The WinMM API requires that the sysex buffer be requeued after - // input of each sysex message. Even if we are ignoring sysex - // messages, we still need to requeue the buffer in case the user - // decides to not ignore sysex messages in the future. However, - // it seems that WinMM calls this function with an empty sysex - // buffer when an application closes and in this case, we should - // avoid requeueing it, else the computer suddenly reboots after - // one or two minutes. - if ( apiData->sysexBuffer[sysex->dwUser]->dwBytesRecorded > 0 ) { - //if ( sysex->dwBytesRecorded > 0 ) { - EnterCriticalSection( &(apiData->_mutex) ); - MMRESULT result = midiInAddBuffer( apiData->inHandle, apiData->sysexBuffer[sysex->dwUser], sizeof(MIDIHDR) ); - LeaveCriticalSection( &(apiData->_mutex) ); - if ( result != MMSYSERR_NOERROR ) - std::cerr << "\nRtMidiIn::midiInputCallback: error sending sysex to Midi device!!\n\n"; - - if ( data->ignoreFlags & 0x01 ) return; - } - else return; - } - - // Save the time of the last non-filtered message - apiData->lastTime = timestamp; - - if ( data->usingCallback ) { - RtMidiIn::RtMidiCallback callback = (RtMidiIn::RtMidiCallback) data->userCallback; - callback( apiData->message.timeStamp, &apiData->message.bytes, data->userData ); - } - else { - // As long as we haven't reached our queue size limit, push the message. - if ( !data->queue.push( apiData->message ) ) - std::cerr << "\nMidiInWinMM: message queue limit reached!!\n\n"; - } - - // Clear the vector for the next input message. - apiData->message.bytes.clear(); -} - -MidiInWinMM :: MidiInWinMM( const std::string &clientName, unsigned int queueSizeLimit ) - : MidiInApi( queueSizeLimit ) -{ - MidiInWinMM::initialize( clientName ); -} - -MidiInWinMM :: ~MidiInWinMM() -{ - // Close a connection if it exists. - MidiInWinMM::closePort(); - - WinMidiData *data = static_cast (apiData_); - DeleteCriticalSection( &(data->_mutex) ); - - // Cleanup. - delete data; -} - -void MidiInWinMM :: initialize( const std::string& /*clientName*/ ) -{ - // We'll issue a warning here if no devices are available but not - // throw an error since the user can plugin something later. - unsigned int nDevices = midiInGetNumDevs(); - if ( nDevices == 0 ) { - errorString_ = "MidiInWinMM::initialize: no MIDI input devices currently available."; - error( RtMidiError::WARNING, errorString_ ); - } - - // Save our api-specific connection information. - WinMidiData *data = (WinMidiData *) new WinMidiData; - apiData_ = (void *) data; - inputData_.apiData = (void *) data; - data->message.bytes.clear(); // needs to be empty for first input message - - if ( !InitializeCriticalSectionAndSpinCount( &(data->_mutex), 0x00000400 ) ) { - errorString_ = "MidiInWinMM::initialize: InitializeCriticalSectionAndSpinCount failed."; - error( RtMidiError::WARNING, errorString_ ); - } -} - -void MidiInWinMM :: openPort( unsigned int portNumber, const std::string &/*portName*/ ) -{ - if ( connected_ ) { - errorString_ = "MidiInWinMM::openPort: a valid connection already exists!"; - error( RtMidiError::WARNING, errorString_ ); - return; - } - - unsigned int nDevices = midiInGetNumDevs(); - if (nDevices == 0) { - errorString_ = "MidiInWinMM::openPort: no MIDI input sources found!"; - error( RtMidiError::NO_DEVICES_FOUND, errorString_ ); - return; - } - - if ( portNumber >= nDevices ) { - std::ostringstream ost; - ost << "MidiInWinMM::openPort: the 'portNumber' argument (" << portNumber << ") is invalid."; - errorString_ = ost.str(); - error( RtMidiError::INVALID_PARAMETER, errorString_ ); - return; - } - - WinMidiData *data = static_cast (apiData_); - MMRESULT result = midiInOpen( &data->inHandle, - portNumber, - (DWORD_PTR)&midiInputCallback, - (DWORD_PTR)&inputData_, - CALLBACK_FUNCTION ); - if ( result != MMSYSERR_NOERROR ) { - errorString_ = "MidiInWinMM::openPort: error creating Windows MM MIDI input port."; - error( RtMidiError::DRIVER_ERROR, errorString_ ); - return; - } - - // Allocate and init the sysex buffers. - for ( int i=0; isysexBuffer[i] = (MIDIHDR*) new char[ sizeof(MIDIHDR) ]; - data->sysexBuffer[i]->lpData = new char[ RT_SYSEX_BUFFER_SIZE ]; - data->sysexBuffer[i]->dwBufferLength = RT_SYSEX_BUFFER_SIZE; - data->sysexBuffer[i]->dwUser = i; // We use the dwUser parameter as buffer indicator - data->sysexBuffer[i]->dwFlags = 0; - - result = midiInPrepareHeader( data->inHandle, data->sysexBuffer[i], sizeof(MIDIHDR) ); - if ( result != MMSYSERR_NOERROR ) { - midiInClose( data->inHandle ); - data->inHandle = 0; - errorString_ = "MidiInWinMM::openPort: error starting Windows MM MIDI input port (PrepareHeader)."; - error( RtMidiError::DRIVER_ERROR, errorString_ ); - return; - } - - // Register the buffer. - result = midiInAddBuffer( data->inHandle, data->sysexBuffer[i], sizeof(MIDIHDR) ); - if ( result != MMSYSERR_NOERROR ) { - midiInClose( data->inHandle ); - data->inHandle = 0; - errorString_ = "MidiInWinMM::openPort: error starting Windows MM MIDI input port (AddBuffer)."; - error( RtMidiError::DRIVER_ERROR, errorString_ ); - return; - } - } - - result = midiInStart( data->inHandle ); - if ( result != MMSYSERR_NOERROR ) { - midiInClose( data->inHandle ); - data->inHandle = 0; - errorString_ = "MidiInWinMM::openPort: error starting Windows MM MIDI input port."; - error( RtMidiError::DRIVER_ERROR, errorString_ ); - return; - } - - connected_ = true; -} - -void MidiInWinMM :: openVirtualPort( const std::string &/*portName*/ ) -{ - // This function cannot be implemented for the Windows MM MIDI API. - errorString_ = "MidiInWinMM::openVirtualPort: cannot be implemented in Windows MM MIDI API!"; - error( RtMidiError::WARNING, errorString_ ); -} - -void MidiInWinMM :: closePort( void ) -{ - if ( connected_ ) { - WinMidiData *data = static_cast (apiData_); - EnterCriticalSection( &(data->_mutex) ); - midiInReset( data->inHandle ); - midiInStop( data->inHandle ); - - for ( int i=0; iinHandle, data->sysexBuffer[i], sizeof(MIDIHDR)); - delete [] data->sysexBuffer[i]->lpData; - delete [] data->sysexBuffer[i]; - if ( result != MMSYSERR_NOERROR ) { - midiInClose( data->inHandle ); - data->inHandle = 0; - errorString_ = "MidiInWinMM::openPort: error closing Windows MM MIDI input port (midiInUnprepareHeader)."; - error( RtMidiError::DRIVER_ERROR, errorString_ ); - return; - } - } - - midiInClose( data->inHandle ); - data->inHandle = 0; - connected_ = false; - LeaveCriticalSection( &(data->_mutex) ); - } -} - -void MidiInWinMM :: setClientName ( const std::string& ) -{ - - errorString_ = "MidiInWinMM::setClientName: this function is not implemented for the WINDOWS_MM API!"; - error( RtMidiError::WARNING, errorString_ ); - -} - -void MidiInWinMM :: setPortName ( const std::string& ) -{ - - errorString_ = "MidiInWinMM::setPortName: this function is not implemented for the WINDOWS_MM API!"; - error( RtMidiError::WARNING, errorString_ ); - -} - -unsigned int MidiInWinMM :: getPortCount() -{ - return midiInGetNumDevs(); -} - -std::string MidiInWinMM :: getPortName( unsigned int portNumber ) -{ - std::string stringName; - unsigned int nDevices = midiInGetNumDevs(); - if ( portNumber >= nDevices ) { - std::ostringstream ost; - ost << "MidiInWinMM::getPortName: the 'portNumber' argument (" << portNumber << ") is invalid."; - errorString_ = ost.str(); - error( RtMidiError::WARNING, errorString_ ); - return stringName; - } - - MIDIINCAPS deviceCaps; - midiInGetDevCaps( portNumber, &deviceCaps, sizeof(MIDIINCAPS)); - stringName = ConvertToUTF8( deviceCaps.szPname ); - - // Next lines added to add the portNumber to the name so that - // the device's names are sure to be listed with individual names - // even when they have the same brand name -#ifndef RTMIDI_DO_NOT_ENSURE_UNIQUE_PORTNAMES - std::ostringstream os; - os << " "; - os << portNumber; - stringName += os.str(); -#endif - - return stringName; -} - -//*********************************************************************// -// API: Windows MM -// Class Definitions: MidiOutWinMM -//*********************************************************************// - -MidiOutWinMM :: MidiOutWinMM( const std::string &clientName ) : MidiOutApi() -{ - MidiOutWinMM::initialize( clientName ); -} - -MidiOutWinMM :: ~MidiOutWinMM() -{ - // Close a connection if it exists. - MidiOutWinMM::closePort(); - - // Cleanup. - WinMidiData *data = static_cast (apiData_); - delete data; -} - -void MidiOutWinMM :: initialize( const std::string& /*clientName*/ ) -{ - // We'll issue a warning here if no devices are available but not - // throw an error since the user can plug something in later. - unsigned int nDevices = midiOutGetNumDevs(); - if ( nDevices == 0 ) { - errorString_ = "MidiOutWinMM::initialize: no MIDI output devices currently available."; - error( RtMidiError::WARNING, errorString_ ); - } - - // Save our api-specific connection information. - WinMidiData *data = (WinMidiData *) new WinMidiData; - apiData_ = (void *) data; -} - -unsigned int MidiOutWinMM :: getPortCount() -{ - return midiOutGetNumDevs(); -} - -std::string MidiOutWinMM :: getPortName( unsigned int portNumber ) -{ - std::string stringName; - unsigned int nDevices = midiOutGetNumDevs(); - if ( portNumber >= nDevices ) { - std::ostringstream ost; - ost << "MidiOutWinMM::getPortName: the 'portNumber' argument (" << portNumber << ") is invalid."; - errorString_ = ost.str(); - error( RtMidiError::WARNING, errorString_ ); - return stringName; - } - - MIDIOUTCAPS deviceCaps; - midiOutGetDevCaps( portNumber, &deviceCaps, sizeof( MIDIOUTCAPS ) ); - stringName = ConvertToUTF8( deviceCaps.szPname ); - - // Next lines added to add the portNumber to the name so that - // the device's names are sure to be listed with individual names - // even when they have the same brand name - std::ostringstream os; -#ifndef RTMIDI_DO_NOT_ENSURE_UNIQUE_PORTNAMES - os << " "; - os << portNumber; - stringName += os.str(); -#endif - - return stringName; -} - -void MidiOutWinMM :: openPort( unsigned int portNumber, const std::string &/*portName*/ ) -{ - if ( connected_ ) { - errorString_ = "MidiOutWinMM::openPort: a valid connection already exists!"; - error( RtMidiError::WARNING, errorString_ ); - return; - } - - unsigned int nDevices = midiOutGetNumDevs(); - if ( nDevices < 1 ) { - errorString_ = "MidiOutWinMM::openPort: no MIDI output destinations found!"; - error( RtMidiError::NO_DEVICES_FOUND, errorString_ ); - return; - } - - if ( portNumber >= nDevices ) { - std::ostringstream ost; - ost << "MidiOutWinMM::openPort: the 'portNumber' argument (" << portNumber << ") is invalid."; - errorString_ = ost.str(); - error( RtMidiError::INVALID_PARAMETER, errorString_ ); - return; - } - - WinMidiData *data = static_cast (apiData_); - MMRESULT result = midiOutOpen( &data->outHandle, - portNumber, - (DWORD)NULL, - (DWORD)NULL, - CALLBACK_NULL ); - if ( result != MMSYSERR_NOERROR ) { - errorString_ = "MidiOutWinMM::openPort: error creating Windows MM MIDI output port."; - error( RtMidiError::DRIVER_ERROR, errorString_ ); - return; - } - - connected_ = true; -} - -void MidiOutWinMM :: closePort( void ) -{ - if ( connected_ ) { - WinMidiData *data = static_cast (apiData_); - midiOutReset( data->outHandle ); - midiOutClose( data->outHandle ); - data->outHandle = 0; - connected_ = false; - } -} - -void MidiOutWinMM :: setClientName ( const std::string& ) -{ - - errorString_ = "MidiOutWinMM::setClientName: this function is not implemented for the WINDOWS_MM API!"; - error( RtMidiError::WARNING, errorString_ ); - -} - -void MidiOutWinMM :: setPortName ( const std::string& ) -{ - - errorString_ = "MidiOutWinMM::setPortName: this function is not implemented for the WINDOWS_MM API!"; - error( RtMidiError::WARNING, errorString_ ); - -} - -void MidiOutWinMM :: openVirtualPort( const std::string &/*portName*/ ) -{ - // This function cannot be implemented for the Windows MM MIDI API. - errorString_ = "MidiOutWinMM::openVirtualPort: cannot be implemented in Windows MM MIDI API!"; - error( RtMidiError::WARNING, errorString_ ); -} - -void MidiOutWinMM :: sendMessage( const unsigned char *message, size_t size ) -{ - if ( !connected_ ) return; - - unsigned int nBytes = static_cast(size); - if ( nBytes == 0 ) { - errorString_ = "MidiOutWinMM::sendMessage: message argument is empty!"; - error( RtMidiError::WARNING, errorString_ ); - return; - } - - MMRESULT result; - WinMidiData *data = static_cast (apiData_); - if ( message[0] == 0xF0 ) { // Sysex message - - // Allocate buffer for sysex data. - char *buffer = (char *) malloc( nBytes ); - if ( buffer == NULL ) { - errorString_ = "MidiOutWinMM::sendMessage: error allocating sysex message memory!"; - error( RtMidiError::MEMORY_ERROR, errorString_ ); - return; - } - - // Copy data to buffer. - for ( unsigned int i=0; ioutHandle, &sysex, sizeof( MIDIHDR ) ); - if ( result != MMSYSERR_NOERROR ) { - free( buffer ); - errorString_ = "MidiOutWinMM::sendMessage: error preparing sysex header."; - error( RtMidiError::DRIVER_ERROR, errorString_ ); - return; - } - - // Send the message. - result = midiOutLongMsg( data->outHandle, &sysex, sizeof( MIDIHDR ) ); - if ( result != MMSYSERR_NOERROR ) { - free( buffer ); - errorString_ = "MidiOutWinMM::sendMessage: error sending sysex message."; - error( RtMidiError::DRIVER_ERROR, errorString_ ); - return; - } - - // Unprepare the buffer and MIDIHDR. - while ( MIDIERR_STILLPLAYING == midiOutUnprepareHeader( data->outHandle, &sysex, sizeof ( MIDIHDR ) ) ) Sleep( 1 ); - free( buffer ); - } - else { // Channel or system message. - - // Make sure the message size isn't too big. - if ( nBytes > 3 ) { - errorString_ = "MidiOutWinMM::sendMessage: message size is greater than 3 bytes (and not sysex)!"; - error( RtMidiError::WARNING, errorString_ ); - return; - } - - // Pack MIDI bytes into double word. - DWORD packet; - unsigned char *ptr = (unsigned char *) &packet; - for ( unsigned int i=0; ioutHandle, packet ); - if ( result != MMSYSERR_NOERROR ) { - errorString_ = "MidiOutWinMM::sendMessage: error sending MIDI message."; - error( RtMidiError::DRIVER_ERROR, errorString_ ); - } - } -} - -#endif // __WINDOWS_MM__ - - -//*********************************************************************// -// API: UNIX JACK -// -// Written primarily by Alexander Svetalkin, with updates for delta -// time by Gary Scavone, April 2011. -// -// *********************************************************************// - -#if defined(__UNIX_JACK__) - -// JACK header files -#include -#include -#include -#ifdef HAVE_SEMAPHORE - #include -#endif - -#define JACK_RINGBUFFER_SIZE 16384 // Default size for ringbuffer - -struct JackMidiData { - jack_client_t *client; - jack_port_t *port; - jack_ringbuffer_t *buffSize; - jack_ringbuffer_t *buffMessage; - jack_time_t lastTime; -#ifdef HAVE_SEMAPHORE - sem_t sem_cleanup; - sem_t sem_needpost; -#endif - MidiInApi :: RtMidiInData *rtMidiIn; - }; - -//*********************************************************************// -// API: JACK -// Class Definitions: MidiInJack -//*********************************************************************// - -static int jackProcessIn( jack_nframes_t nframes, void *arg ) -{ - JackMidiData *jData = (JackMidiData *) arg; - MidiInApi :: RtMidiInData *rtData = jData->rtMidiIn; - jack_midi_event_t event; - jack_time_t time; - - // Is port created? - if ( jData->port == NULL ) return 0; - - void *buff = jack_port_get_buffer( jData->port, nframes ); - bool& continueSysex = rtData->continueSysex; - unsigned char& ignoreFlags = rtData->ignoreFlags; - - // We have midi events in buffer - int evCount = jack_midi_get_event_count( buff ); - for (int j = 0; j < evCount; j++) { - MidiInApi::MidiMessage& message = rtData->message; - jack_midi_event_get( &event, buff, j ); - - // Compute the delta time. - time = jack_get_time(); - if ( rtData->firstMessage == true ) { - message.timeStamp = 0.0; - rtData->firstMessage = false; - } else - message.timeStamp = ( time - jData->lastTime ) * 0.000001; - - jData->lastTime = time; - - if ( !continueSysex ) - message.bytes.clear(); - - if ( !( ( continueSysex || event.buffer[0] == 0xF0 ) && ( ignoreFlags & 0x01 ) ) ) { - // Unless this is a (possibly continued) SysEx message and we're ignoring SysEx, - // copy the event buffer into the MIDI message struct. - for ( unsigned int i = 0; i < event.size; i++ ) - message.bytes.push_back( event.buffer[i] ); - } - - switch ( event.buffer[0] ) { - case 0xF0: - // Start of a SysEx message - continueSysex = event.buffer[event.size - 1] != 0xF7; - if ( ignoreFlags & 0x01 ) continue; - break; - case 0xF1: - case 0xF8: - // MIDI Time Code or Timing Clock message - if ( ignoreFlags & 0x02 ) continue; - break; - case 0xFE: - // Active Sensing message - if ( ignoreFlags & 0x04 ) continue; - break; - default: - if ( continueSysex ) { - // Continuation of a SysEx message - continueSysex = event.buffer[event.size - 1] != 0xF7; - if ( ignoreFlags & 0x01 ) continue; - } - // All other MIDI messages - } - - if ( !continueSysex ) { - // If not a continuation of a SysEx message, - // invoke the user callback function or queue the message. - if ( rtData->usingCallback ) { - RtMidiIn::RtMidiCallback callback = (RtMidiIn::RtMidiCallback) rtData->userCallback; - callback( message.timeStamp, &message.bytes, rtData->userData ); - } - else { - // As long as we haven't reached our queue size limit, push the message. - if ( !rtData->queue.push( message ) ) - std::cerr << "\nMidiInJack: message queue limit reached!!\n\n"; - } - } - } - - return 0; -} - -MidiInJack :: MidiInJack( const std::string &clientName, unsigned int queueSizeLimit ) - : MidiInApi( queueSizeLimit ) -{ - MidiInJack::initialize( clientName ); -} - -void MidiInJack :: initialize( const std::string& clientName ) -{ - JackMidiData *data = new JackMidiData; - apiData_ = (void *) data; - - data->rtMidiIn = &inputData_; - data->port = NULL; - data->client = NULL; - this->clientName = clientName; - - connect(); -} - -void MidiInJack :: connect() -{ - JackMidiData *data = static_cast (apiData_); - if ( data->client ) - return; - - // Initialize JACK client - if (( data->client = jack_client_open( clientName.c_str(), JackNoStartServer, NULL )) == 0) { - errorString_ = "MidiInJack::initialize: JACK server not running?"; - error( RtMidiError::WARNING, errorString_ ); - return; - } - - jack_set_process_callback( data->client, jackProcessIn, data ); - jack_activate( data->client ); -} - -MidiInJack :: ~MidiInJack() -{ - JackMidiData *data = static_cast (apiData_); - MidiInJack::closePort(); - - if ( data->client ) - jack_client_close( data->client ); - delete data; -} - -void MidiInJack :: openPort( unsigned int portNumber, const std::string &portName ) -{ - JackMidiData *data = static_cast (apiData_); - - connect(); - - // Creating new port - if ( data->port == NULL ) - data->port = jack_port_register( data->client, portName.c_str(), - JACK_DEFAULT_MIDI_TYPE, JackPortIsInput, 0 ); - - if ( data->port == NULL ) { - errorString_ = "MidiInJack::openPort: JACK error creating port"; - error( RtMidiError::DRIVER_ERROR, errorString_ ); - return; - } - - // Connecting to the output - std::string name = getPortName( portNumber ); - jack_connect( data->client, name.c_str(), jack_port_name( data->port ) ); - - connected_ = true; -} - -void MidiInJack :: openVirtualPort( const std::string &portName ) -{ - JackMidiData *data = static_cast (apiData_); - - connect(); - if ( data->port == NULL ) - data->port = jack_port_register( data->client, portName.c_str(), - JACK_DEFAULT_MIDI_TYPE, JackPortIsInput, 0 ); - - if ( data->port == NULL ) { - errorString_ = "MidiInJack::openVirtualPort: JACK error creating virtual port"; - error( RtMidiError::DRIVER_ERROR, errorString_ ); - } -} - -unsigned int MidiInJack :: getPortCount() -{ - int count = 0; - JackMidiData *data = static_cast (apiData_); - connect(); - if ( !data->client ) - return 0; - - // List of available ports - const char **ports = jack_get_ports( data->client, NULL, JACK_DEFAULT_MIDI_TYPE, JackPortIsOutput ); - - if ( ports == NULL ) return 0; - while ( ports[count] != NULL ) - count++; - - free( ports ); - - return count; -} - -std::string MidiInJack :: getPortName( unsigned int portNumber ) -{ - JackMidiData *data = static_cast (apiData_); - std::string retStr( "" ); - - connect(); - - // List of available ports - const char **ports = jack_get_ports( data->client, NULL, - JACK_DEFAULT_MIDI_TYPE, JackPortIsOutput ); - - // Check port validity - if ( ports == NULL ) { - errorString_ = "MidiInJack::getPortName: no ports available!"; - error( RtMidiError::WARNING, errorString_ ); - return retStr; - } - - unsigned int i; - for ( i=0; i (apiData_); - - if ( data->port == NULL ) return; - jack_port_unregister( data->client, data->port ); - data->port = NULL; - - connected_ = false; -} - -void MidiInJack:: setClientName( const std::string& ) -{ - - errorString_ = "MidiInJack::setClientName: this function is not implemented for the UNIX_JACK API!"; - error( RtMidiError::WARNING, errorString_ ); - -} - -void MidiInJack :: setPortName( const std::string &portName ) -{ - JackMidiData *data = static_cast (apiData_); -#ifdef JACK_HAS_PORT_RENAME - jack_port_rename( data->client, data->port, portName.c_str() ); -#else - jack_port_set_name( data->port, portName.c_str() ); -#endif -} - -//*********************************************************************// -// API: JACK -// Class Definitions: MidiOutJack -//*********************************************************************// - -// Jack process callback -static int jackProcessOut( jack_nframes_t nframes, void *arg ) -{ - JackMidiData *data = (JackMidiData *) arg; - jack_midi_data_t *midiData; - int space; - - // Is port created? - if ( data->port == NULL ) return 0; - - void *buff = jack_port_get_buffer( data->port, nframes ); - jack_midi_clear_buffer( buff ); - - while ( jack_ringbuffer_read_space( data->buffSize ) > 0 ) { - jack_ringbuffer_read( data->buffSize, (char *) &space, (size_t) sizeof( space ) ); - midiData = jack_midi_event_reserve( buff, 0, space ); - - jack_ringbuffer_read( data->buffMessage, (char *) midiData, (size_t) space ); - } - -#ifdef HAVE_SEMAPHORE - if ( !sem_trywait( &data->sem_needpost ) ) - sem_post( &data->sem_cleanup ); -#endif - - return 0; -} - -MidiOutJack :: MidiOutJack( const std::string &clientName ) : MidiOutApi() -{ - MidiOutJack::initialize( clientName ); -} - -void MidiOutJack :: initialize( const std::string& clientName ) -{ - JackMidiData *data = new JackMidiData; - apiData_ = (void *) data; - - data->port = NULL; - data->client = NULL; -#ifdef HAVE_SEMAPHORE - sem_init( &data->sem_cleanup, 0, 0 ); - sem_init( &data->sem_needpost, 0, 0 ); -#endif - this->clientName = clientName; - - connect(); -} - -void MidiOutJack :: connect() -{ - JackMidiData *data = static_cast (apiData_); - if ( data->client ) - return; - - // Initialize output ringbuffers - data->buffSize = jack_ringbuffer_create( JACK_RINGBUFFER_SIZE ); - data->buffMessage = jack_ringbuffer_create( JACK_RINGBUFFER_SIZE ); - - // Initialize JACK client - if ( ( data->client = jack_client_open( clientName.c_str(), JackNoStartServer, NULL ) ) == 0 ) { - errorString_ = "MidiOutJack::initialize: JACK server not running?"; - error( RtMidiError::WARNING, errorString_ ); - return; - } - - jack_set_process_callback( data->client, jackProcessOut, data ); - jack_activate( data->client ); -} - -MidiOutJack :: ~MidiOutJack() -{ - JackMidiData *data = static_cast (apiData_); - MidiOutJack::closePort(); - - // Cleanup - jack_ringbuffer_free( data->buffSize ); - jack_ringbuffer_free( data->buffMessage ); - if ( data->client ) { - jack_client_close( data->client ); - } - -#ifdef HAVE_SEMAPHORE - sem_destroy( &data->sem_cleanup ); - sem_destroy( &data->sem_needpost ); -#endif - - delete data; -} - -void MidiOutJack :: openPort( unsigned int portNumber, const std::string &portName ) -{ - JackMidiData *data = static_cast (apiData_); - - connect(); - - // Creating new port - if ( data->port == NULL ) - data->port = jack_port_register( data->client, portName.c_str(), - JACK_DEFAULT_MIDI_TYPE, JackPortIsOutput, 0 ); - - if ( data->port == NULL ) { - errorString_ = "MidiOutJack::openPort: JACK error creating port"; - error( RtMidiError::DRIVER_ERROR, errorString_ ); - return; - } - - // Connecting to the output - std::string name = getPortName( portNumber ); - jack_connect( data->client, jack_port_name( data->port ), name.c_str() ); - - connected_ = true; -} - -void MidiOutJack :: openVirtualPort( const std::string &portName ) -{ - JackMidiData *data = static_cast (apiData_); - - connect(); - if ( data->port == NULL ) - data->port = jack_port_register( data->client, portName.c_str(), - JACK_DEFAULT_MIDI_TYPE, JackPortIsOutput, 0 ); - - if ( data->port == NULL ) { - errorString_ = "MidiOutJack::openVirtualPort: JACK error creating virtual port"; - error( RtMidiError::DRIVER_ERROR, errorString_ ); - } -} - -unsigned int MidiOutJack :: getPortCount() -{ - int count = 0; - JackMidiData *data = static_cast (apiData_); - connect(); - if ( !data->client ) - return 0; - - // List of available ports - const char **ports = jack_get_ports( data->client, NULL, - JACK_DEFAULT_MIDI_TYPE, JackPortIsInput ); - - if ( ports == NULL ) return 0; - while ( ports[count] != NULL ) - count++; - - free( ports ); - - return count; -} - -std::string MidiOutJack :: getPortName( unsigned int portNumber ) -{ - JackMidiData *data = static_cast (apiData_); - std::string retStr(""); - - connect(); - - // List of available ports - const char **ports = jack_get_ports( data->client, NULL, - JACK_DEFAULT_MIDI_TYPE, JackPortIsInput ); - - // Check port validity - if ( ports == NULL ) { - errorString_ = "MidiOutJack::getPortName: no ports available!"; - error( RtMidiError::WARNING, errorString_ ); - return retStr; - } - - if ( ports[portNumber] == NULL ) { - std::ostringstream ost; - ost << "MidiOutJack::getPortName: the 'portNumber' argument (" << portNumber << ") is invalid."; - errorString_ = ost.str(); - error( RtMidiError::WARNING, errorString_ ); - } - else retStr.assign( ports[portNumber] ); - - free( ports ); - return retStr; -} - -void MidiOutJack :: closePort() -{ - JackMidiData *data = static_cast (apiData_); - - if ( data->port == NULL ) return; - -#ifdef HAVE_SEMAPHORE - struct timespec ts; - if ( clock_gettime( CLOCK_REALTIME, &ts ) != -1 ) { - ts.tv_sec += 1; // wait max one second - sem_post( &data->sem_needpost ); - sem_timedwait( &data->sem_cleanup, &ts ); - } -#endif - - jack_port_unregister( data->client, data->port ); - data->port = NULL; - - connected_ = false; -} - -void MidiOutJack:: setClientName( const std::string& ) -{ - - errorString_ = "MidiOutJack::setClientName: this function is not implemented for the UNIX_JACK API!"; - error( RtMidiError::WARNING, errorString_ ); - -} - -void MidiOutJack :: setPortName( const std::string &portName ) -{ - JackMidiData *data = static_cast (apiData_); -#ifdef JACK_HAS_PORT_RENAME - jack_port_rename( data->client, data->port, portName.c_str() ); -#else - jack_port_set_name( data->port, portName.c_str() ); -#endif -} - -void MidiOutJack :: sendMessage( const unsigned char *message, size_t size ) -{ - int nBytes = static_cast(size); - JackMidiData *data = static_cast (apiData_); - - // Write full message to buffer - jack_ringbuffer_write( data->buffMessage, ( const char * ) message, nBytes ); - jack_ringbuffer_write( data->buffSize, ( char * ) &nBytes, sizeof( nBytes ) ); -} - -#endif // __UNIX_JACK__ diff -Nru bambootracker-0.4.5+git20121209/BambooTracker/midi/RtMidi/RtMidi.hpp bambootracker-0.4.6/BambooTracker/midi/RtMidi/RtMidi.hpp --- bambootracker-0.4.5+git20121209/BambooTracker/midi/RtMidi/RtMidi.hpp 2020-12-08 14:05:28.000000000 +0000 +++ bambootracker-0.4.6/BambooTracker/midi/RtMidi/RtMidi.hpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,633 +0,0 @@ -/**********************************************************************/ -/*! \class RtMidi - \brief An abstract base class for realtime MIDI input/output. - - This class implements some common functionality for the realtime - MIDI input/output subclasses RtMidiIn and RtMidiOut. - - RtMidi GitHub site: https://github.com/thestk/rtmidi - RtMidi WWW site: http://www.music.mcgill.ca/~gary/rtmidi/ - - RtMidi: realtime MIDI i/o C++ classes - Copyright (c) 2003-2019 Gary P. Scavone - - Permission is hereby granted, free of charge, to any person - obtaining a copy of this software and associated documentation files - (the "Software"), to deal in the Software without restriction, - including without limitation the rights to use, copy, modify, merge, - publish, distribute, sublicense, and/or sell copies of the Software, - and to permit persons to whom the Software is furnished to do so, - subject to the following conditions: - - The above copyright notice and this permission notice shall be - included in all copies or substantial portions of the Software. - - Any person wishing to distribute modifications to the Software is - asked to send the modifications to the original developer so that - they can be incorporated into the canonical version. This is, - however, not a binding provision of this license. - - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. - IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR - ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF - CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION - WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -*/ -/**********************************************************************/ - -/*! - \file RtMidi.h - */ - -#ifndef RTMIDI_H -#define RTMIDI_H - -#if defined _WIN32 || defined __CYGWIN__ - #if defined(RTMIDI_EXPORT) - #define RTMIDI_DLL_PUBLIC __declspec(dllexport) - #else - #define RTMIDI_DLL_PUBLIC - #endif -#else - #if __GNUC__ >= 4 - #define RTMIDI_DLL_PUBLIC __attribute__( (visibility( "default" )) ) - #else - #define RTMIDI_DLL_PUBLIC - #endif -#endif - -#define RTMIDI_VERSION "4.0.0" - -#include -#include -#include -#include - -/************************************************************************/ -/*! \class RtMidiError - \brief Exception handling class for RtMidi. - - The RtMidiError class is quite simple but it does allow errors to be - "caught" by RtMidiError::Type. See the RtMidi documentation to know - which methods can throw an RtMidiError. -*/ -/************************************************************************/ - -class RTMIDI_DLL_PUBLIC RtMidiError : public std::exception -{ - public: - //! Defined RtMidiError types. - enum Type { - WARNING, /*!< A non-critical error. */ - DEBUG_WARNING, /*!< A non-critical error which might be useful for debugging. */ - UNSPECIFIED, /*!< The default, unspecified error type. */ - NO_DEVICES_FOUND, /*!< No devices found on system. */ - INVALID_DEVICE, /*!< An invalid device ID was specified. */ - MEMORY_ERROR, /*!< An error occured during memory allocation. */ - INVALID_PARAMETER, /*!< An invalid parameter was specified to a function. */ - INVALID_USE, /*!< The function was called incorrectly. */ - DRIVER_ERROR, /*!< A system driver error occured. */ - SYSTEM_ERROR, /*!< A system error occured. */ - THREAD_ERROR /*!< A thread error occured. */ - }; - - //! The constructor. - RtMidiError( const std::string& message, Type type = RtMidiError::UNSPECIFIED ) throw() - : message_(message), type_(type) {} - - //! The destructor. - virtual ~RtMidiError( void ) throw() {} - - //! Prints thrown error message to stderr. - virtual void printMessage( void ) const throw() { std::cerr << '\n' << message_ << "\n\n"; } - - //! Returns the thrown error message type. - virtual const Type& getType( void ) const throw() { return type_; } - - //! Returns the thrown error message string. - virtual const std::string& getMessage( void ) const throw() { return message_; } - - //! Returns the thrown error message as a c-style string. - virtual const char* what( void ) const throw() { return message_.c_str(); } - - protected: - std::string message_; - Type type_; -}; - -//! RtMidi error callback function prototype. -/*! - \param type Type of error. - \param errorText Error description. - - Note that class behaviour is undefined after a critical error (not - a warning) is reported. - */ -typedef void (*RtMidiErrorCallback)( RtMidiError::Type type, const std::string &errorText, void *userData ); - -class MidiApi; - -class RTMIDI_DLL_PUBLIC RtMidi -{ - public: - //! MIDI API specifier arguments. - enum Api { - UNSPECIFIED, /*!< Search for a working compiled API. */ - MACOSX_CORE, /*!< Macintosh OS-X CoreMIDI API. */ - LINUX_ALSA, /*!< The Advanced Linux Sound Architecture API. */ - UNIX_JACK, /*!< The JACK Low-Latency MIDI Server API. */ - WINDOWS_MM, /*!< The Microsoft Multimedia MIDI API. */ - RTMIDI_DUMMY, /*!< A compilable but non-functional API. */ - NUM_APIS /*!< Number of values in this enum. */ - }; - - //! A static function to determine the current RtMidi version. - static std::string getVersion( void ) throw(); - - //! A static function to determine the available compiled MIDI APIs. - /*! - The values returned in the std::vector can be compared against - the enumerated list values. Note that there can be more than one - API compiled for certain operating systems. - */ - static void getCompiledApi( std::vector &apis ) throw(); - - //! Return the name of a specified compiled MIDI API. - /*! - This obtains a short lower-case name used for identification purposes. - This value is guaranteed to remain identical across library versions. - If the API is unknown, this function will return the empty string. - */ - static std::string getApiName( RtMidi::Api api ); - - //! Return the display name of a specified compiled MIDI API. - /*! - This obtains a long name used for display purposes. - If the API is unknown, this function will return the empty string. - */ - static std::string getApiDisplayName( RtMidi::Api api ); - - //! Return the compiled MIDI API having the given name. - /*! - A case insensitive comparison will check the specified name - against the list of compiled APIs, and return the one which - matches. On failure, the function returns UNSPECIFIED. - */ - static RtMidi::Api getCompiledApiByName( const std::string &name ); - - //! Pure virtual openPort() function. - virtual void openPort( unsigned int portNumber = 0, const std::string &portName = std::string( "RtMidi" ) ) = 0; - - //! Pure virtual openVirtualPort() function. - virtual void openVirtualPort( const std::string &portName = std::string( "RtMidi" ) ) = 0; - - //! Pure virtual getPortCount() function. - virtual unsigned int getPortCount() = 0; - - //! Pure virtual getPortName() function. - virtual std::string getPortName( unsigned int portNumber = 0 ) = 0; - - //! Pure virtual closePort() function. - virtual void closePort( void ) = 0; - - void setClientName( const std::string &clientName ); - void setPortName( const std::string &portName ); - - //! Returns true if a port is open and false if not. - /*! - Note that this only applies to connections made with the openPort() - function, not to virtual ports. - */ - virtual bool isPortOpen( void ) const = 0; - - //! Set an error callback function to be invoked when an error has occured. - /*! - The callback function will be called whenever an error has occured. It is best - to set the error callback function before opening a port. - */ - virtual void setErrorCallback( RtMidiErrorCallback errorCallback = NULL, void *userData = 0 ) = 0; - - protected: - RtMidi(); - virtual ~RtMidi(); - MidiApi *rtapi_; -}; - -/**********************************************************************/ -/*! \class RtMidiIn - \brief A realtime MIDI input class. - - This class provides a common, platform-independent API for - realtime MIDI input. It allows access to a single MIDI input - port. Incoming MIDI messages are either saved to a queue for - retrieval using the getMessage() function or immediately passed to - a user-specified callback function. Create multiple instances of - this class to connect to more than one MIDI device at the same - time. With the OS-X, Linux ALSA, and JACK MIDI APIs, it is also - possible to open a virtual input port to which other MIDI software - clients can connect. - - by Gary P. Scavone, 2003-2017. -*/ -/**********************************************************************/ - -// **************************************************************** // -// -// RtMidiIn and RtMidiOut class declarations. -// -// RtMidiIn / RtMidiOut are "controllers" used to select an available -// MIDI input or output interface. They present common APIs for the -// user to call but all functionality is implemented by the classes -// MidiInApi, MidiOutApi and their subclasses. RtMidiIn and RtMidiOut -// each create an instance of a MidiInApi or MidiOutApi subclass based -// on the user's API choice. If no choice is made, they attempt to -// make a "logical" API selection. -// -// **************************************************************** // - -class RTMIDI_DLL_PUBLIC RtMidiIn : public RtMidi -{ - public: - - //! User callback function type definition. - typedef void (*RtMidiCallback)( double timeStamp, std::vector *message, void *userData ); - - //! Default constructor that allows an optional api, client name and queue size. - /*! - An exception will be thrown if a MIDI system initialization - error occurs. The queue size defines the maximum number of - messages that can be held in the MIDI queue (when not using a - callback function). If the queue size limit is reached, - incoming messages will be ignored. - - If no API argument is specified and multiple API support has been - compiled, the default order of use is ALSA, JACK (Linux) and CORE, - JACK (OS-X). - - \param api An optional API id can be specified. - \param clientName An optional client name can be specified. This - will be used to group the ports that are created - by the application. - \param queueSizeLimit An optional size of the MIDI input queue can be specified. - */ - RtMidiIn( RtMidi::Api api=UNSPECIFIED, - const std::string& clientName = "RtMidi Input Client", - unsigned int queueSizeLimit = 100 ); - - //! If a MIDI connection is still open, it will be closed by the destructor. - ~RtMidiIn ( void ) throw(); - - //! Returns the MIDI API specifier for the current instance of RtMidiIn. - RtMidi::Api getCurrentApi( void ) throw(); - - //! Open a MIDI input connection given by enumeration number. - /*! - \param portNumber An optional port number greater than 0 can be specified. - Otherwise, the default or first port found is opened. - \param portName An optional name for the application port that is used to connect to portId can be specified. - */ - void openPort( unsigned int portNumber = 0, const std::string &portName = std::string( "RtMidi Input" ) ); - - //! Create a virtual input port, with optional name, to allow software connections (OS X, JACK and ALSA only). - /*! - This function creates a virtual MIDI input port to which other - software applications can connect. This type of functionality - is currently only supported by the Macintosh OS-X, any JACK, - and Linux ALSA APIs (the function returns an error for the other APIs). - - \param portName An optional name for the application port that is - used to connect to portId can be specified. - */ - void openVirtualPort( const std::string &portName = std::string( "RtMidi Input" ) ); - - //! Set a callback function to be invoked for incoming MIDI messages. - /*! - The callback function will be called whenever an incoming MIDI - message is received. While not absolutely necessary, it is best - to set the callback function before opening a MIDI port to avoid - leaving some messages in the queue. - - \param callback A callback function must be given. - \param userData Optionally, a pointer to additional data can be - passed to the callback function whenever it is called. - */ - void setCallback( RtMidiCallback callback, void *userData = 0 ); - - //! Cancel use of the current callback function (if one exists). - /*! - Subsequent incoming MIDI messages will be written to the queue - and can be retrieved with the \e getMessage function. - */ - void cancelCallback(); - - //! Close an open MIDI connection (if one exists). - void closePort( void ); - - //! Returns true if a port is open and false if not. - /*! - Note that this only applies to connections made with the openPort() - function, not to virtual ports. - */ - virtual bool isPortOpen() const; - - //! Return the number of available MIDI input ports. - /*! - \return This function returns the number of MIDI ports of the selected API. - */ - unsigned int getPortCount(); - - //! Return a string identifier for the specified MIDI input port number. - /*! - \return The name of the port with the given Id is returned. - \retval An empty string is returned if an invalid port specifier - is provided. User code should assume a UTF-8 encoding. - */ - std::string getPortName( unsigned int portNumber = 0 ); - - //! Specify whether certain MIDI message types should be queued or ignored during input. - /*! - By default, MIDI timing and active sensing messages are ignored - during message input because of their relative high data rates. - MIDI sysex messages are ignored by default as well. Variable - values of "true" imply that the respective message type will be - ignored. - */ - void ignoreTypes( bool midiSysex = true, bool midiTime = true, bool midiSense = true ); - - //! Fill the user-provided vector with the data bytes for the next available MIDI message in the input queue and return the event delta-time in seconds. - /*! - This function returns immediately whether a new message is - available or not. A valid message is indicated by a non-zero - vector size. An exception is thrown if an error occurs during - message retrieval or an input connection was not previously - established. - */ - double getMessage( std::vector *message ); - - //! Set an error callback function to be invoked when an error has occured. - /*! - The callback function will be called whenever an error has occured. It is best - to set the error callback function before opening a port. - */ - virtual void setErrorCallback( RtMidiErrorCallback errorCallback = NULL, void *userData = 0 ); - - protected: - void openMidiApi( RtMidi::Api api, const std::string &clientName, unsigned int queueSizeLimit ); -}; - -/**********************************************************************/ -/*! \class RtMidiOut - \brief A realtime MIDI output class. - - This class provides a common, platform-independent API for MIDI - output. It allows one to probe available MIDI output ports, to - connect to one such port, and to send MIDI bytes immediately over - the connection. Create multiple instances of this class to - connect to more than one MIDI device at the same time. With the - OS-X, Linux ALSA and JACK MIDI APIs, it is also possible to open a - virtual port to which other MIDI software clients can connect. - - by Gary P. Scavone, 2003-2017. -*/ -/**********************************************************************/ - -class RTMIDI_DLL_PUBLIC RtMidiOut : public RtMidi -{ - public: - //! Default constructor that allows an optional client name. - /*! - An exception will be thrown if a MIDI system initialization error occurs. - - If no API argument is specified and multiple API support has been - compiled, the default order of use is ALSA, JACK (Linux) and CORE, - JACK (OS-X). - */ - RtMidiOut( RtMidi::Api api=UNSPECIFIED, - const std::string& clientName = "RtMidi Output Client" ); - - //! The destructor closes any open MIDI connections. - ~RtMidiOut( void ) throw(); - - //! Returns the MIDI API specifier for the current instance of RtMidiOut. - RtMidi::Api getCurrentApi( void ) throw(); - - //! Open a MIDI output connection. - /*! - An optional port number greater than 0 can be specified. - Otherwise, the default or first port found is opened. An - exception is thrown if an error occurs while attempting to make - the port connection. - */ - void openPort( unsigned int portNumber = 0, const std::string &portName = std::string( "RtMidi Output" ) ); - - //! Close an open MIDI connection (if one exists). - void closePort( void ); - - //! Returns true if a port is open and false if not. - /*! - Note that this only applies to connections made with the openPort() - function, not to virtual ports. - */ - virtual bool isPortOpen() const; - - //! Create a virtual output port, with optional name, to allow software connections (OS X, JACK and ALSA only). - /*! - This function creates a virtual MIDI output port to which other - software applications can connect. This type of functionality - is currently only supported by the Macintosh OS-X, Linux ALSA - and JACK APIs (the function does nothing with the other APIs). - An exception is thrown if an error occurs while attempting to - create the virtual port. - */ - void openVirtualPort( const std::string &portName = std::string( "RtMidi Output" ) ); - - //! Return the number of available MIDI output ports. - unsigned int getPortCount( void ); - - //! Return a string identifier for the specified MIDI port type and number. - /*! - \return The name of the port with the given Id is returned. - \retval An empty string is returned if an invalid port specifier - is provided. User code should assume a UTF-8 encoding. - */ - std::string getPortName( unsigned int portNumber = 0 ); - - //! Immediately send a single message out an open MIDI output port. - /*! - An exception is thrown if an error occurs during output or an - output connection was not previously established. - */ - void sendMessage( const std::vector *message ); - - //! Immediately send a single message out an open MIDI output port. - /*! - An exception is thrown if an error occurs during output or an - output connection was not previously established. - - \param message A pointer to the MIDI message as raw bytes - \param size Length of the MIDI message in bytes - */ - void sendMessage( const unsigned char *message, size_t size ); - - //! Set an error callback function to be invoked when an error has occured. - /*! - The callback function will be called whenever an error has occured. It is best - to set the error callback function before opening a port. - */ - virtual void setErrorCallback( RtMidiErrorCallback errorCallback = NULL, void *userData = 0 ); - - protected: - void openMidiApi( RtMidi::Api api, const std::string &clientName ); -}; - - -// **************************************************************** // -// -// MidiInApi / MidiOutApi class declarations. -// -// Subclasses of MidiInApi and MidiOutApi contain all API- and -// OS-specific code necessary to fully implement the RtMidi API. -// -// Note that MidiInApi and MidiOutApi are abstract base classes and -// cannot be explicitly instantiated. RtMidiIn and RtMidiOut will -// create instances of a MidiInApi or MidiOutApi subclass. -// -// **************************************************************** // - -class RTMIDI_DLL_PUBLIC MidiApi -{ - public: - - MidiApi(); - virtual ~MidiApi(); - virtual RtMidi::Api getCurrentApi( void ) = 0; - virtual void openPort( unsigned int portNumber, const std::string &portName ) = 0; - virtual void openVirtualPort( const std::string &portName ) = 0; - virtual void closePort( void ) = 0; - virtual void setClientName( const std::string &clientName ) = 0; - virtual void setPortName( const std::string &portName ) = 0; - - virtual unsigned int getPortCount( void ) = 0; - virtual std::string getPortName( unsigned int portNumber ) = 0; - - inline bool isPortOpen() const { return connected_; } - void setErrorCallback( RtMidiErrorCallback errorCallback, void *userData ); - - //! A basic error reporting function for RtMidi classes. - void error( RtMidiError::Type type, std::string errorString ); - -protected: - virtual void initialize( const std::string& clientName ) = 0; - - void *apiData_; - bool connected_; - std::string errorString_; - RtMidiErrorCallback errorCallback_; - bool firstErrorOccurred_; - void *errorCallbackUserData_; -}; - -class RTMIDI_DLL_PUBLIC MidiInApi : public MidiApi -{ - public: - - MidiInApi( unsigned int queueSizeLimit ); - virtual ~MidiInApi( void ); - void setCallback( RtMidiIn::RtMidiCallback callback, void *userData ); - void cancelCallback( void ); - virtual void ignoreTypes( bool midiSysex, bool midiTime, bool midiSense ); - double getMessage( std::vector *message ); - - // A MIDI structure used internally by the class to store incoming - // messages. Each message represents one and only one MIDI message. - struct MidiMessage { - std::vector bytes; - - //! Time in seconds elapsed since the previous message - double timeStamp; - - // Default constructor. - MidiMessage() - : bytes(0), timeStamp(0.0) {} - }; - - struct MidiQueue { - unsigned int front; - unsigned int back; - unsigned int ringSize; - MidiMessage *ring; - - // Default constructor. - MidiQueue() - : front(0), back(0), ringSize(0), ring(0) {} - bool push( const MidiMessage& ); - bool pop( std::vector*, double* ); - unsigned int size( unsigned int *back=0, unsigned int *front=0 ); - }; - - // The RtMidiInData structure is used to pass private class data to - // the MIDI input handling function or thread. - struct RtMidiInData { - MidiQueue queue; - MidiMessage message; - unsigned char ignoreFlags; - bool doInput; - bool firstMessage; - void *apiData; - bool usingCallback; - RtMidiIn::RtMidiCallback userCallback; - void *userData; - bool continueSysex; - - // Default constructor. - RtMidiInData() - : ignoreFlags(7), doInput(false), firstMessage(true), apiData(0), usingCallback(false), - userCallback(0), userData(0), continueSysex(false) {} - }; - - protected: - RtMidiInData inputData_; -}; - -class RTMIDI_DLL_PUBLIC MidiOutApi : public MidiApi -{ - public: - - MidiOutApi( void ); - virtual ~MidiOutApi( void ); - virtual void sendMessage( const unsigned char *message, size_t size ) = 0; -}; - -// **************************************************************** // -// -// Inline RtMidiIn and RtMidiOut definitions. -// -// **************************************************************** // - -inline RtMidi::Api RtMidiIn :: getCurrentApi( void ) throw() { return rtapi_->getCurrentApi(); } -inline void RtMidiIn :: openPort( unsigned int portNumber, const std::string &portName ) { rtapi_->openPort( portNumber, portName ); } -inline void RtMidiIn :: openVirtualPort( const std::string &portName ) { rtapi_->openVirtualPort( portName ); } -inline void RtMidiIn :: closePort( void ) { rtapi_->closePort(); } -inline bool RtMidiIn :: isPortOpen() const { return rtapi_->isPortOpen(); } -inline void RtMidiIn :: setCallback( RtMidiCallback callback, void *userData ) { static_cast(rtapi_)->setCallback( callback, userData ); } -inline void RtMidiIn :: cancelCallback( void ) { static_cast(rtapi_)->cancelCallback(); } -inline unsigned int RtMidiIn :: getPortCount( void ) { return rtapi_->getPortCount(); } -inline std::string RtMidiIn :: getPortName( unsigned int portNumber ) { return rtapi_->getPortName( portNumber ); } -inline void RtMidiIn :: ignoreTypes( bool midiSysex, bool midiTime, bool midiSense ) { static_cast(rtapi_)->ignoreTypes( midiSysex, midiTime, midiSense ); } -inline double RtMidiIn :: getMessage( std::vector *message ) { return static_cast(rtapi_)->getMessage( message ); } -inline void RtMidiIn :: setErrorCallback( RtMidiErrorCallback errorCallback, void *userData ) { rtapi_->setErrorCallback(errorCallback, userData); } - -inline RtMidi::Api RtMidiOut :: getCurrentApi( void ) throw() { return rtapi_->getCurrentApi(); } -inline void RtMidiOut :: openPort( unsigned int portNumber, const std::string &portName ) { rtapi_->openPort( portNumber, portName ); } -inline void RtMidiOut :: openVirtualPort( const std::string &portName ) { rtapi_->openVirtualPort( portName ); } -inline void RtMidiOut :: closePort( void ) { rtapi_->closePort(); } -inline bool RtMidiOut :: isPortOpen() const { return rtapi_->isPortOpen(); } -inline unsigned int RtMidiOut :: getPortCount( void ) { return rtapi_->getPortCount(); } -inline std::string RtMidiOut :: getPortName( unsigned int portNumber ) { return rtapi_->getPortName( portNumber ); } -inline void RtMidiOut :: sendMessage( const std::vector *message ) { static_cast(rtapi_)->sendMessage( &message->at(0), message->size() ); } -inline void RtMidiOut :: sendMessage( const unsigned char *message, size_t size ) { static_cast(rtapi_)->sendMessage( message, size ); } -inline void RtMidiOut :: setErrorCallback( RtMidiErrorCallback errorCallback, void *userData ) { rtapi_->setErrorCallback(errorCallback, userData); } - -#endif diff -Nru bambootracker-0.4.5+git20121209/BambooTracker/midi/RtMidi/RtMidi.pri bambootracker-0.4.6/BambooTracker/midi/RtMidi/RtMidi.pri --- bambootracker-0.4.5+git20121209/BambooTracker/midi/RtMidi/RtMidi.pri 2020-12-08 14:05:28.000000000 +0000 +++ bambootracker-0.4.6/BambooTracker/midi/RtMidi/RtMidi.pri 1970-01-01 00:00:00.000000000 +0000 @@ -1,48 +0,0 @@ -SOURCES += \ - $$PWD/RtMidi.cpp - -HEADERS += \ - $$PWD/RtMidi.hpp - -win32 { - DEFINES += __WINDOWS_MM__ - LIBS += -lwinmm -} -else:macx { - DEFINES += __MACOSX_CORE__ - LIBS += -framework CoreMIDI -framework CoreAudio -framework CoreFoundation - - use_jack { - DEFINES += __UNIX_JACK__ - LIBS += -ljack - jack_has_rename { - DEFINES += JACK_HAS_PORT_RENAME - } - } -} -else:linux { - DEFINES += __LINUX_ALSA__ - LIBS += -lasound - - use_jack { - DEFINES += __UNIX_JACK__ - LIBS += -ljack - jack_has_rename { - DEFINES += JACK_HAS_PORT_RENAME - } - } -} -else:bsd { - # OSS does not offer MIDI functionalities, only fallback to dummy interface - use_alsa { - DEFINES += __LINUX_ALSA__ - LIBS += -lasound - } - use_jack { - DEFINES += __UNIX_JACK__ - LIBS += -ljack - jack_has_rename { - DEFINES += JACK_HAS_PORT_RENAME - } - } -} diff -Nru bambootracker-0.4.5+git20121209/BambooTracker/misc.hpp bambootracker-0.4.6/BambooTracker/misc.hpp --- bambootracker-0.4.5+git20121209/BambooTracker/misc.hpp 2020-12-08 14:05:28.000000000 +0000 +++ bambootracker-0.4.6/BambooTracker/misc.hpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,246 +0,0 @@ -/* - * Copyright (C) 2018-2020 Rerrah - * - * Permission is hereby granted, free of charge, to any person - * obtaining a copy of this software and associated documentation - * files (the "Software"), to deal in the Software without - * restriction, including without limitation the rights to use, - * copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following - * conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES - * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT - * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, - * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR - * OTHER DEALINGS IN THE SOFTWARE. - */ - -#pragma once - -#include -#include -#include -#include - -#if !defined(DECL_MAYBE_UNUSED) && defined(__GNUC__) -#define DECL_MAYBE_UNUSED __attribute__((unused)) -#elif !defined(DECL_MAYBE_UNUSED) -#define DECL_MAYBE_UNUSED -#endif - -enum class SoundSource : int -{ - FM = 1, - SSG = 2, - RHYTHM = 4, - ADPCM = 8 -}; - -enum class SongType : int -{ - Standard, - FM3chExpanded -}; - -enum class Note : int -{ - C = 0, - CS = 32, - D = 64, - DS = 96, - E = 128, - F = 160, - FS = 192, - G = 224, - GS = 256, - A = 288, - AS = 320, - B = 352 -}; - -enum class FMEnvelopeTextType : int -{ - Skip, AL, FB, - AR1, DR1, SR1, RR1, SL1, TL1, KS1, ML1, DT1, - AR2, DR2, SR2, RR2, SL2, TL2, KS2, ML2, DT2, - AR3, DR3, SR3, RR3, SL3, TL3, KS3, ML3, DT3, - AR4, DR4, SR4, RR4, SL4, TL4, KS4, ML4, DT4 -}; - -enum class FMOperatorType : int -{ - All, Op1, Op2, Op3, Op4 -}; - -enum class SSGWaveformType : int -{ - UNSET = -1, - SQUARE = 0, - TRIANGLE = 1, - SAW = 2, - INVSAW = 3, - SQM_TRIANGLE = 4, - SQM_SAW = 5, - SQM_INVSAW = 6 -}; - -enum class EffectDisplayControl -{ - Unset, ReverseFMVolumeDelay, ReverseFMBrightness -}; - -enum class RealChipInterface : int -{ - NONE = 0, - SCCI = 1, - C86CTL = 2 -}; - -DECL_MAYBE_UNUSED -static std::pair noteNumberToOctaveAndNote(int num) -{ - if (num < 0) return std::make_pair(0, Note::C); - - int oct = num / 12; - if (oct > 7) return std::make_pair(7, Note::B); - - Note note; - switch (num % 12) { - case 0: note = Note::C; break; - case 1: note = Note::CS; break; - case 2: note = Note::D; break; - case 3: note = Note::DS; break; - case 4: note = Note::E; break; - case 5: note = Note::F; break; - case 6: note = Note::FS; break; - case 7: note = Note::G; break; - case 8: note = Note::GS; break; - case 9: note = Note::A; break; - case 10: note = Note::AS; break; - case 11: note = Note::B; break; - } - - return std::make_pair(oct, note); -} - -DECL_MAYBE_UNUSED -static int octaveAndNoteToNoteNumber(int octave, Note note) -{ - int ret = 12 * octave; - - switch (note) { - case Note::C: ret += 0; break; - case Note::CS: ret += 1; break; - case Note::D: ret += 2; break; - case Note::DS: ret += 3; break; - case Note::E: ret += 4; break; - case Note::F: ret += 5; break; - case Note::FS: ret += 6; break; - case Note::G: ret += 7; break; - case Note::GS: ret += 8; break; - case Note::A: ret += 9; break; - case Note::AS: ret += 10; break; - case Note::B: ret += 11; break; - } - - return ret; -} - -DECL_MAYBE_UNUSED -static int ctohex(const char c) -{ - if (c == '0') return 0; - else if (c == '1') return 1; - else if (c == '2') return 2; - else if (c == '3') return 3; - else if (c == '4') return 4; - else if (c == '5') return 5; - else if (c == '6') return 6; - else if (c == '7') return 7; - else if (c == '8') return 8; - else if (c == '9') return 9; - else if (c == 'A') return 10; - else if (c == 'B') return 11; - else if (c == 'C') return 12; - else if (c == 'D') return 13; - else if (c == 'E') return 14; - else if (c == 'F') return 15; - else return -1; -} - -DECL_MAYBE_UNUSED -static uint8_t uitobcd(const uint8_t v) -{ - if (v > 99) throw std::out_of_range("Out of range."); - - uint8_t high = v / 10; - uint8_t low = v % 10; - return static_cast(high << 4) + low; -} - -DECL_MAYBE_UNUSED -static uint8_t bcdtoui(const uint8_t v) -{ - uint8_t high = v >> 4; - uint8_t low = v & 0x0f; - return high * 10 + low; -} - -DECL_MAYBE_UNUSED -inline static size_t getFMChannelCount(SongType type) -{ - switch (type) { - case SongType::Standard: return 6; - case SongType::FM3chExpanded: return 9; - default: throw std::invalid_argument("Invalid SongType."); - } -} - -DECL_MAYBE_UNUSED -inline static bool isModulatedWaveformSSG(SSGWaveformType type) -{ - switch (type) { - case SSGWaveformType::SQUARE: - case SSGWaveformType::TRIANGLE: - case SSGWaveformType::SAW: - case SSGWaveformType::INVSAW: - return false; - case SSGWaveformType::SQM_TRIANGLE: - case SSGWaveformType::SQM_SAW: - case SSGWaveformType::SQM_INVSAW: - return true; - default: - throw std::invalid_argument("Invalid SSGWaveformType"); - } -} - -DECL_MAYBE_UNUSED -inline static bool isModulatedWaveformSSG(int id) -{ - try { - return isModulatedWaveformSSG(static_cast(id)); - } catch (...) { - throw std::invalid_argument("Invalid id"); - } -} - -template -DECL_MAYBE_UNUSED -inline const T& clamp(const T& value, const T& low, const T& high) -{ - return std::min(std::max(value, low), high); -} - -DECL_MAYBE_UNUSED -inline static int calcADPCMDeltaN(unsigned int rate) -{ - return static_cast(std::round((rate << 16) / 55500.)); -} diff -Nru bambootracker-0.4.5+git20121209/BambooTracker/module/effect.cpp bambootracker-0.4.6/BambooTracker/module/effect.cpp --- bambootracker-0.4.5+git20121209/BambooTracker/module/effect.cpp 2020-12-08 14:05:28.000000000 +0000 +++ bambootracker-0.4.6/BambooTracker/module/effect.cpp 2021-02-11 14:37:17.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (C) 2019-2020 Rerrah + * Copyright (C) 2019-2021 Rerrah * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation @@ -24,8 +24,34 @@ */ #include "effect.hpp" +#include +#include "step.hpp" -EffectType Effect::toEffectType(SoundSource src, std::string id) +namespace +{ +const std::unordered_map CTOHEX = { + { '0', 0 }, + { '1', 1 }, + { '2', 2 }, + { '3', 3 }, + { '4', 4 }, + { '5', 5 }, + { '6', 6 }, + { '7', 7 }, + { '8', 8 }, + { '9', 9 }, + { 'A', 10 }, + { 'B', 11 }, + { 'C', 12 }, + { 'D', 13 }, + { 'E', 14 }, + { 'F', 15 } +}; +} + +namespace effect_utils +{ +EffectType validateEffectId(SoundSource src, const std::string& id) { if (id == "00") { switch (src) { @@ -292,22 +318,22 @@ } } -Effect Effect::makeEffectData(SoundSource src, std::string id, int value) +Effect validateEffect(SoundSource src, const std::string& id, int value) { - if (value == -1) return { EffectType::NoEffect, -1 }; + if (value == Step::EFF_VAL_NONE) return { EffectType::NoEffect, Step::EFF_VAL_NONE }; - EffectType type = Effect::toEffectType(src, id); + EffectType type = effect_utils::validateEffectId(src, id); int v; switch (type) { case EffectType::NoEffect: - v = -1; + v = Step::EFF_VAL_NONE; break; case EffectType::VolumeDelay: case EffectType::TLControl: case EffectType::ARControl: case EffectType::DRControl: - v = (ctohex(id[1]) << 8) | value; + v = (CTOHEX.at(id[1]) << 8) | value; break; default: v = value; @@ -315,3 +341,4 @@ return { type, v }; } +} diff -Nru bambootracker-0.4.5+git20121209/BambooTracker/module/effect.hpp bambootracker-0.4.6/BambooTracker/module/effect.hpp --- bambootracker-0.4.5+git20121209/BambooTracker/module/effect.hpp 2020-12-08 14:05:28.000000000 +0000 +++ bambootracker-0.4.6/BambooTracker/module/effect.hpp 2021-02-11 14:37:17.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (C) 2019-2020 Rerrah + * Copyright (C) 2019-2021 Rerrah * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation @@ -26,7 +26,8 @@ #pragma once #include -#include "misc.hpp" +#include "step.hpp" +#include "bamboo_tracker_defs.hpp" enum class EffectType { @@ -42,7 +43,25 @@ { EffectType type; int value; - - static EffectType toEffectType(SoundSource src, std::string id); - static Effect makeEffectData(SoundSource src, std::string id, int value); }; + +namespace effect_utils +{ +EffectType validateEffectId(SoundSource src, const std::string& id); +Effect validateEffect(SoundSource src, const std::string& id, int value); +inline Effect validateEffect(SoundSource src, const Step::PlainEffect& plain) +{ + return validateEffect(src, plain.id, plain.value); +} + +inline int reverseFmVolume(int volume, bool over0 = false) noexcept +{ + return (volume < bt_defs::NSTEP_FM_VOLUME) ? (bt_defs::NSTEP_FM_VOLUME - 1 - volume) + : over0 ? 0 : volume; +} + +inline int reverseFmBrightness(int value) noexcept +{ + return (value > 0) ? (0xff - value + 1) : value; +} +} diff -Nru bambootracker-0.4.5+git20121209/BambooTracker/module/groove.cpp bambootracker-0.4.6/BambooTracker/module/groove.cpp --- bambootracker-0.4.5+git20121209/BambooTracker/module/groove.cpp 2020-12-08 14:05:28.000000000 +0000 +++ bambootracker-0.4.6/BambooTracker/module/groove.cpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,47 +0,0 @@ -/* - * Copyright (C) 2018 Rerrah - * - * Permission is hereby granted, free of charge, to any person - * obtaining a copy of this software and associated documentation - * files (the "Software"), to deal in the Software without - * restriction, including without limitation the rights to use, - * copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following - * conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES - * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT - * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, - * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR - * OTHER DEALINGS IN THE SOFTWARE. - */ - -#include "groove.hpp" -#include - -Groove::Groove() - : seq_{ 6, 6 } -{ -} - -Groove::Groove(std::vector seq) - : seq_(std::move(seq)) -{ -} - -void Groove::setSequrnce(std::vector seq) -{ - seq_ = std::move(seq); -} - -std::vector Groove::getSequence() const -{ - return seq_; -} diff -Nru bambootracker-0.4.5+git20121209/BambooTracker/module/groove.hpp bambootracker-0.4.6/BambooTracker/module/groove.hpp --- bambootracker-0.4.5+git20121209/BambooTracker/module/groove.hpp 2020-12-08 14:05:28.000000000 +0000 +++ bambootracker-0.4.6/BambooTracker/module/groove.hpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,40 +0,0 @@ -/* - * Copyright (C) 2018 Rerrah - * - * Permission is hereby granted, free of charge, to any person - * obtaining a copy of this software and associated documentation - * files (the "Software"), to deal in the Software without - * restriction, including without limitation the rights to use, - * copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following - * conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES - * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT - * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, - * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR - * OTHER DEALINGS IN THE SOFTWARE. - */ - -#pragma once - -#include - -class Groove -{ -public: - Groove(); - Groove(std::vector seq); - void setSequrnce(std::vector seq); - std::vector getSequence() const; - -private: - std::vector seq_; -}; diff -Nru bambootracker-0.4.5+git20121209/BambooTracker/module/module.cpp bambootracker-0.4.6/BambooTracker/module/module.cpp --- bambootracker-0.4.5+git20121209/BambooTracker/module/module.cpp 2020-12-08 14:05:28.000000000 +0000 +++ bambootracker-0.4.6/BambooTracker/module/module.cpp 2021-02-11 14:37:17.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (C) 2018-2019 Rerrah + * Copyright (C) 2018-2021 Rerrah * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation @@ -26,9 +26,11 @@ #include "module.hpp" #include #include +#include "utils.hpp" -Module::Module(std::string filePath, std::string title, std::string author, std::string copyright, - std::string comment, unsigned int tickFreq) +Module::Module(const std::string& filePath, const std::string& title, + const std::string& author, const std::string& copyright, + const std::string& comment, unsigned int tickFreq) : filePath_(filePath), title_(title), author_(author), @@ -42,87 +44,7 @@ customLevelSSG_(0) { songs_.emplace_back(0); - grooves_.emplace_back(); -} - -void Module::setFilePath(std::string path) -{ - filePath_ = path; -} - -std::string Module::getFilePath() const -{ - return filePath_; -} - -void Module::setTitle(std::string title) -{ - title_ = title; -} - -std::string Module::getTitle() const -{ - return title_; -} - -void Module::setAuthor(std::string author) -{ - author_ = author; -} - -std::string Module::getAuthor() const -{ - return author_; -} - -void Module::setCopyright(std::string copyright) -{ - copyright_ = copyright; -} - -std::string Module::getCopyright() const -{ - return copyright_; -} - -void Module::setComment(std::string comment) -{ - comment_ = comment; -} - -std::string Module::getComment() const -{ - return comment_; -} - -void Module::setTickFrequency(unsigned int freq) -{ - tickFreq_ = freq; -} - -unsigned int Module::getTickFrequency() const -{ - return tickFreq_; -} - -void Module::setStepHighlight1Distance(size_t dist) -{ - stepHl1Dist_ = dist; -} - -size_t Module::getStepHighlight1Distance() const -{ - return stepHl1Dist_; -} - -void Module::setStepHighlight2Distance(size_t dist) -{ - stepHl2Dist_ = dist; -} - -size_t Module::getStepHighlight2Distance() const -{ - return stepHl2Dist_; + addGroove(); } size_t Module::getSongCount() const @@ -135,43 +57,12 @@ return grooves_.size(); } -void Module::setMixerType(MixerType type) -{ - mixType_ = type; -} - -MixerType Module::getMixerType() const +void Module::addSong(SongType songType, const std::string& title) { - return mixType_; + songs_.emplace_back(static_cast(songs_.size()), songType, title); } -void Module::setCustomMixerFMLevel(double level) -{ - customLevelFM_ = level; -} - -double Module::getCustomMixerFMLevel() const -{ - return customLevelFM_; -} - -void Module::setCustomMixerSSGLevel(double level) -{ - customLevelSSG_ = level; -} - -double Module::getCustomMixerSSGLevel() const -{ - return customLevelSSG_; -} - -void Module::addSong(SongType songType, std::string title) -{ - int n = static_cast(songs_.size()); - songs_.emplace_back(n, songType, title); -} - -void Module::addSong(int n, SongType songType, std::string title, bool isUsedTempo, +void Module::addSong(int n, SongType songType, const std::string& title, bool isUsedTempo, int tempo, int groove, int speed, size_t defaultPatternSize) { if (n < static_cast(songs_.size())) @@ -182,7 +73,7 @@ n, songType, title, isUsedTempo, tempo, groove, speed, defaultPatternSize); } -void Module::sortSongs(std::vector numbers) +void Module::sortSongs(const std::vector& numbers) { std::vector newSongs; newSongs.reserve(songs_.size()); @@ -193,21 +84,18 @@ newSongs.push_back(*it); } - songs_.assign(std::make_move_iterator(newSongs.begin()), - std::make_move_iterator(newSongs.end())); - songs_.shrink_to_fit(); + songs_ = std::move(newSongs); } Song& Module::getSong(int num) { - auto it = std::find_if(songs_.begin(), songs_.end(), - [num](Song& s) { return s.getNumber() == num; }); - return *it; + return *utils::findIf(songs_, [num](Song& s) { return s.getNumber() == num; });; } void Module::addGroove() { - grooves_.emplace_back(); + // Default groove is "6 6" + grooves_.push_back({ 6, 6 }); } void Module::removeGroove(int num) @@ -215,41 +103,37 @@ grooves_.erase(grooves_.begin() + num); } -void Module::setGroove(int num, std::vector seq) +void Module::setGroove(int num, const std::vector& seq) { - grooves_.at(static_cast(num)).setSequrnce(seq); + grooves_.at(static_cast(num)) = seq; } -void Module::setGrooves(std::vector> seqs) +void Module::setGrooves(const std::vector>& seqs) { - grooves_.clear(); - for (auto& seq : seqs) { - grooves_.emplace_back(seq); - } + grooves_ = seqs; } -Groove& Module::getGroove(int num) +Groove Module::getGroove(int num) const { return grooves_.at(static_cast(num)); } -std::unordered_set Module::getRegisterdInstruments() const +std::set Module::getRegisterdInstruments() const { - std::unordered_set set; - for (auto& song : songs_) { - for (auto& n : song.getRegisteredInstruments()) { - set.insert(n); - } + std::set set; + for (const Song& song : songs_) { + auto&& subset = song.getRegisteredInstruments(); + std::copy(subset.begin(), subset.end(), std::inserter(set, set.end())); } return set; } void Module::clearUnusedPatterns() { - for (auto& song : songs_) song.clearUnusedPatterns(); + for (Song& song : songs_) song.clearUnusedPatterns(); } -void Module::replaceDuplicateInstrumentsInPatterns(std::unordered_map map) +void Module::replaceDuplicateInstrumentsInPatterns(const std::unordered_map& map) { - for (auto& song : songs_) song.replaceDuplicateInstrumentsInPatterns(map); + for (Song& song : songs_) song.replaceDuplicateInstrumentsInPatterns(map); } diff -Nru bambootracker-0.4.5+git20121209/BambooTracker/module/module.hpp bambootracker-0.4.6/BambooTracker/module/module.hpp --- bambootracker-0.4.5+git20121209/BambooTracker/module/module.hpp 2020-12-08 14:05:28.000000000 +0000 +++ bambootracker-0.4.6/BambooTracker/module/module.hpp 2021-02-11 14:37:17.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (C) 2018-2019 Rerrah + * Copyright (C) 2018-2020 Rerrah * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation @@ -26,11 +26,12 @@ #pragma once #include -#include +#include #include #include #include "song.hpp" -#include "groove.hpp" + +using Groove = std::vector; enum class MixerType : int { @@ -45,50 +46,51 @@ class Module { public: - Module(std::string filePath = "", std::string title = u8"", std::string author = u8"", - std::string copyright = u8"", std::string comment = u8"", unsigned int tickFreq = 60); - - void setFilePath(std::string path); - std::string getFilePath() const; - void setTitle(std::string title); - std::string getTitle() const; - void setAuthor(std::string author); - std::string getAuthor() const; - void setCopyright(std::string copyright); - std::string getCopyright() const; - void setComment(std::string comment); - std::string getComment() const; - void setTickFrequency(unsigned int freq); - unsigned int getTickFrequency() const; - void setStepHighlight1Distance(size_t dist); - size_t getStepHighlight1Distance() const; - void setStepHighlight2Distance(size_t dist); - size_t getStepHighlight2Distance() const; + Module(const std::string& filePath = "", const std::string& title = u8"", + const std::string& author = u8"", const std::string& copyright = u8"", + const std::string& comment = u8"", unsigned int tickFreq = 60); + + inline void setFilePath(const std::string& path) { filePath_ = path; } + std::string getFilePath() const noexcept { return filePath_; }; + inline void setTitle(const std::string& title) { title_ = title; } + inline std::string getTitle() const noexcept { return title_; } + inline void setAuthor(const std::string& author) { author_ = author; } + inline std::string getAuthor() const noexcept { return author_; } + inline void setCopyright(const std::string& copyright) { copyright_ = copyright; } + inline std::string getCopyright() const noexcept { return copyright_; } + inline void setComment(const std::string& comment) { comment_ = comment; } + inline std::string getComment() const noexcept { return comment_; } + inline void setTickFrequency(unsigned int freq) { tickFreq_ = freq; } + inline unsigned int getTickFrequency() const noexcept { return tickFreq_; } + inline void setStepHighlight1Distance(size_t dist) { stepHl1Dist_ = dist; } + inline size_t getStepHighlight1Distance() const noexcept { return stepHl1Dist_; } + inline void setStepHighlight2Distance(size_t dist) { stepHl2Dist_ = dist; } + inline size_t getStepHighlight2Distance() const noexcept { return stepHl2Dist_; } size_t getSongCount() const; size_t getGrooveCount() const; - void setMixerType(MixerType type); - MixerType getMixerType() const; - void setCustomMixerFMLevel(double level); - double getCustomMixerFMLevel() const; - void setCustomMixerSSGLevel(double level); - double getCustomMixerSSGLevel() const; + inline void setMixerType(MixerType type) { mixType_ = type; } + inline MixerType getMixerType() const noexcept { return mixType_; } + inline void setCustomMixerFMLevel(double level) { customLevelFM_ = level; } + inline double getCustomMixerFMLevel() const noexcept { return customLevelFM_; } + inline void setCustomMixerSSGLevel(double level) { customLevelSSG_ = level; } + inline double getCustomMixerSSGLevel() const noexcept { return customLevelSSG_; } - void addSong(SongType songType, std::string title); - void addSong(int n, SongType songType, std::string title, bool isUsedTempo, + void addSong(SongType songType, const std::string& title); + void addSong(int n, SongType songType, const std::string& title, bool isUsedTempo, int tempo, int groove, int speed, size_t defaultPatternSize); - void sortSongs(std::vector numbers); + void sortSongs(const std::vector& numbers); Song& getSong(int num); void addGroove(); void removeGroove(int num); - void setGroove(int num, std::vector seq); - void setGrooves(std::vector> seqs); - Groove& getGroove(int num); + void setGroove(int num, const std::vector& seq); + void setGrooves(const std::vector>& seqs); + Groove getGroove(int num) const; - std::unordered_set getRegisterdInstruments() const; + std::set getRegisterdInstruments() const; void clearUnusedPatterns(); - void replaceDuplicateInstrumentsInPatterns(std::unordered_map map); + void replaceDuplicateInstrumentsInPatterns(const std::unordered_map& map); private: std::string filePath_; diff -Nru bambootracker-0.4.5+git20121209/BambooTracker/module/pattern.cpp bambootracker-0.4.6/BambooTracker/module/pattern.cpp --- bambootracker-0.4.5+git20121209/BambooTracker/module/pattern.cpp 2020-12-08 14:05:28.000000000 +0000 +++ bambootracker-0.4.6/BambooTracker/module/pattern.cpp 2021-02-11 14:37:17.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (C) 2018-2020 Rerrah + * Copyright (C) 2018-2021 Rerrah * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation @@ -24,43 +24,24 @@ */ #include "pattern.hpp" -#include "effect.hpp" -#include "misc.hpp" #include +#include "effect.hpp" +#include "note.hpp" +#include "utils.hpp" -Pattern::Pattern(int n, size_t defSize) - : num_(n), size_(defSize), steps_(defSize), usedCnt_(0) -{ -} - -Pattern::Pattern(int n, size_t size, std::vector steps) - : num_(n), size_(size), steps_(steps), usedCnt_(0) -{ -} - -void Pattern::setNumber(int n) -{ - num_ = n; -} - -int Pattern::getNumber() const -{ - return num_; -} - -int Pattern::usedCountUp() +namespace { - return ++usedCnt_; +constexpr size_t MAX_STEP_SIZE = 256; } -int Pattern::usedCountDown() +Pattern::Pattern(int n, size_t defSize) + : num_(n), size_(defSize), steps_(defSize), usedCnt_(0) { - return --usedCnt_; } -int Pattern::getUsedCount() const +Pattern::Pattern(int n, size_t size, const std::vector& steps) + : num_(n), size_(size), steps_(steps), usedCnt_(0) { - return usedCnt_; } Step& Pattern::getStep(int n) @@ -71,10 +52,10 @@ size_t Pattern::getSize() const { for (size_t i = 0; i < size_; ++i) { - for (int j = 0; j < 4; ++j) { - switch (Effect::makeEffectData( // "SoundSource::FM" is dummy - SoundSource::FM, steps_[i].getEffectID(j), steps_[i].getEffectValue(j) - ).type) { + for (int j = 0; j < Step::N_EFFECT; ++j) { + if (!steps_[i].hasEffectValue(j)) continue; + // "SoundSource::FM" is dummy, these effects are not related with sound source + switch (effect_utils::validateEffectId(SoundSource::FM, steps_[i].getEffectId(j))) { case EffectType::PositionJump: case EffectType::SongEnd: case EffectType::PatternBreak: @@ -89,7 +70,7 @@ void Pattern::changeSize(size_t size) { - if (0 < size && size <= 256) { + if (size && size <= MAX_STEP_SIZE) { size_ = size; if (steps_.size() < size) steps_.resize(size); } @@ -110,30 +91,26 @@ steps_.resize(size_); } -bool Pattern::existCommand() const +bool Pattern::hasEvent() const { - auto endIt = steps_.begin() + static_cast(size_); - auto&& it = std::find_if(steps_.begin(), endIt, - [](const Step& step) { return step.existCommand(); }); - return (it != endIt); + auto endIt = steps_.cbegin() + static_cast(size_); + return std::any_of(steps_.cbegin(), endIt, + [](const Step& step) { return step.hasEvent(); }); } std::vector Pattern::getEditedStepIndices() const { - std::vector list; - for (size_t i = 0; i < size_; ++i) { - if (steps_.at(i).existCommand()) - list.push_back(static_cast(i)); - } - return list; + auto endIt = steps_.cbegin() + static_cast(size_); + return utils::findIndicesIf(steps_.cbegin(), endIt, + [](const Step& step) { return step.hasEvent(); }); } -std::unordered_set Pattern::getRegisteredInstruments() const +std::set Pattern::getRegisteredInstruments() const { - std::unordered_set set; + std::set set; for (size_t i = 0; i < size_; ++i) { - int n = steps_.at(i).getInstrumentNumber(); - if (n > -1) set.insert(n); + const Step& step = steps_.at(i); + if (step.hasInstrument()) set.insert(step.getInstrumentNumber()); } return set; } @@ -143,14 +120,14 @@ return Pattern(asNumber, size_, steps_); } -void Pattern::transpose(int seminotes, std::vector excludeInsts) +void Pattern::transpose(int seminotes, const std::vector& excludeInsts) { for (size_t i = 0; i < size_; ++i) { Step& step = steps_.at(i); int note = step.getNoteNumber(); - if (note > -1 && std::none_of(excludeInsts.begin(), excludeInsts.end(), + if (step.hasGeneralNote() && std::none_of(excludeInsts.begin(), excludeInsts.end(), [a = step.getInstrumentNumber()](int b) { return a == b; })) { - step.setNoteNumber(clamp(note + seminotes, 0, 95)); + step.setNoteNumber(utils::clamp(note + seminotes, 0, Note::NOTE_NUMBER_RANGE - 1)); } } } diff -Nru bambootracker-0.4.5+git20121209/BambooTracker/module/pattern.hpp bambootracker-0.4.6/BambooTracker/module/pattern.hpp --- bambootracker-0.4.5+git20121209/BambooTracker/module/pattern.hpp 2020-12-08 14:05:28.000000000 +0000 +++ bambootracker-0.4.6/BambooTracker/module/pattern.hpp 2021-02-11 14:37:17.000000000 +0000 @@ -26,7 +26,7 @@ #pragma once #include -#include +#include #include #include "step.hpp" @@ -35,12 +35,12 @@ public: Pattern(int n, size_t defSize); - void setNumber(int n); - int getNumber() const; + inline void setNumber(int n) noexcept { num_ = n; } + inline int getNumber() const noexcept { return num_; } - int usedCountUp(); - int usedCountDown(); - int getUsedCount() const; + inline int increaseUsedCount() noexcept { return ++usedCnt_; } + inline int decreaseUsedCount() noexcept { return --usedCnt_; } + inline int getUsedCount() const noexcept { return usedCnt_; } Step& getStep(int n); @@ -50,13 +50,13 @@ void insertStep(int n); void deletePreviousStep(int n); - bool existCommand() const; + bool hasEvent() const; std::vector getEditedStepIndices() const; - std::unordered_set getRegisteredInstruments() const; + std::set getRegisteredInstruments() const; Pattern clone(int asNumber); - void transpose(int seminotes, std::vector excludeInsts); + void transpose(int seminotes, const std::vector& excludeInsts); void clear(); @@ -66,5 +66,5 @@ std::vector steps_; int usedCnt_; - Pattern(int n, size_t size, std::vector steps); + Pattern(int n, size_t size, const std::vector& steps); }; diff -Nru bambootracker-0.4.5+git20121209/BambooTracker/module/song.cpp bambootracker-0.4.6/BambooTracker/module/song.cpp --- bambootracker-0.4.5+git20121209/BambooTracker/module/song.cpp 2020-12-08 14:05:28.000000000 +0000 +++ bambootracker-0.4.6/BambooTracker/module/song.cpp 2021-02-11 14:37:17.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (C) 2018-2020 Rerrah + * Copyright (C) 2018-2021 Rerrah * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation @@ -27,8 +27,15 @@ #include #include #include +#include "bamboo_tracker_defs.hpp" +#include "utils.hpp" -Song::Song(int number, SongType songType, std::string title, bool isUsedTempo, +Bookmark::Bookmark(const std::string& argname, int argorder, int argstep) + : name(argname), order(argorder), step(argstep) +{ +} + +Song::Song(int number, SongType songType, const std::string& title, bool isUsedTempo, int tempo, int groove, int speed, size_t defaultPatternSize) : num_(number), type_(songType), @@ -71,66 +78,6 @@ } } -void Song::setNumber(int n) -{ - num_ = n; -} - -int Song::getNumber() const -{ - return num_; -} - -void Song::setTitle(std::string title) -{ - title_ = title; -} - -std::string Song::getTitle() const -{ - return title_; -} - -void Song::setTempo(int tempo) -{ - tempo_ = tempo; -} - -int Song::getTempo() const -{ - return tempo_; -} - -void Song::setGroove(int groove) -{ - groove_ = groove; -} - -int Song::getGroove() const -{ - return groove_; -} - -void Song::toggleTempoOrGroove(bool isUsedTempo) -{ - isUsedTempo_ = isUsedTempo; -} - -bool Song::isUsedTempo() const -{ - return isUsedTempo_; -} - -void Song::setSpeed(int speed) -{ - speed_ = speed; -} - -int Song::getSpeed() const -{ - return speed_; -} - void Song::setDefaultPatternSize(size_t size) { defPtnSize_ = size; @@ -139,11 +86,6 @@ } } -size_t Song::getDefaultPatternSize() const -{ - return defPtnSize_; -} - size_t Song::getPatternSizeFromOrderNumber(int order) { if (static_cast(getOrderSize()) <= order) return 0; // Ilegal value @@ -167,6 +109,7 @@ std::vector Song::getTrackAttributes() const { std::vector ret; + ret.reserve(tracks_.size()); std::transform(tracks_.begin(), tracks_.end(), std::back_inserter(ret), [](const Track& track) { return track.getAttribute(); }); return ret; @@ -205,11 +148,11 @@ } } -std::vector Song::getOrderData(int order) +std::vector Song::getOrderData(int order) const { - std::vector ret; - for (auto& track : tracks_) { - ret.push_back(track.getOrderData(order)); + std::vector ret; + for (const Track& track : tracks_) { + ret.push_back(track.getOrderInfo(order)); } return ret; } @@ -221,64 +164,87 @@ bool Song::canAddNewOrder() const { - return getOrderSize() < 256; + return tracks_[0].canAddNewOrder(); } void Song::insertOrderBelow(int order) { if (!canAddNewOrder()) return; - for (auto& track : tracks_) { + for (Track& track : tracks_) { track.insertOrderBelow(order); } } void Song::deleteOrder(int order) { - for (auto& track : tracks_) { + for (Track& track : tracks_) { track.deleteOrder(order); } } void Song::swapOrder(int a, int b) { - for (auto& track : tracks_) { + for (Track& track : tracks_) { track.swapOrder(a, b); } } -std::unordered_set Song::getRegisteredInstruments() const +std::set Song::getRegisteredInstruments() const { - std::unordered_set set; - for (auto& track : tracks_) { - for (auto& n : track.getRegisteredInstruments()) { - set.insert(n); - } + std::set set; + for (const Track& track : tracks_) { + auto&& subset = track.getRegisteredInstruments(); + std::copy(subset.begin(), subset.end(), std::inserter(set, set.end())); } return set; } void Song::clearUnusedPatterns() { - for (auto& track : tracks_) track.clearUnusedPatterns(); + for (Track& track : tracks_) track.clearUnusedPatterns(); } -void Song::replaceDuplicateInstrumentsInPatterns(std::unordered_map map) +void Song::replaceDuplicateInstrumentsInPatterns(const std::unordered_map& map) { - for (auto& track : tracks_) track.replaceDuplicateInstrumentsInPatterns(map); + for (Track& track : tracks_) track.replaceDuplicateInstrumentsInPatterns(map); } -int Song::addBookmark(std::string name, int order, int step) +void Song::transpose(int seminotes, const std::vector& excludeInsts) +{ + for (Track& track : tracks_) track.transpose(seminotes, excludeInsts); +} + +void Song::swapTracks(int track1, int track2) +{ + auto it1 = utils::findIf(tracks_, [&](const Track& t) { + return t.getAttribute().number == track1; + }); + if (it1 == tracks_.end()) throw std::invalid_argument("Invalid track number"); + auto it2 = utils::findIf(tracks_, [&](const Track& t) { + return t.getAttribute().number == track2; + }); + if (it2 == tracks_.end()) throw std::invalid_argument("Invalid track number"); + + TrackAttribute attrib1 = it1->getAttribute(); + TrackAttribute attrib2 = it2->getAttribute(); + it1->setAttribute(attrib2.number, attrib2.source, attrib2.channelInSource); + it2->setAttribute(attrib1.number, attrib1.source, attrib1.channelInSource); + + std::iter_swap(it1, it2); +} + +int Song::addBookmark(const std::string& name, int order, int step) { bms_.push_back(Bookmark(name, order, step)); return static_cast(bms_.size() - 1); } -void Song::changeBookmark(int i, std::string name, int order, int step) +void Song::changeBookmark(int i, const std::string& name, int order, int step) { Bookmark& bm = bms_.at(static_cast(i)); bm.name = name; bm.order = order; - bm.step = step; + bm.step = step; } void Song::removeBookmark(int i) @@ -335,7 +301,7 @@ return tmp; } -Bookmark Song::getPreviousBookmark(int order, int step) +Bookmark Song::getPreviousBookmark(int order, int step) const { std::vector list = getSortedBookmarkList(); size_t i = 0; @@ -348,7 +314,7 @@ return list.at((list.size() + i - 1) % list.size()); } -Bookmark Song::getNextBookmark(int order, int step) +Bookmark Song::getNextBookmark(int order, int step) const { std::vector list = getSortedBookmarkList(); size_t i = 0; @@ -365,32 +331,3 @@ { return bms_.size(); } - -void Song::transpose(int seminotes, std::vector excludeInsts) -{ - for (auto& track : tracks_) track.transpose(seminotes, excludeInsts); -} - -void Song::swapTracks(int track1, int track2) -{ - auto it1 = std::find_if(tracks_.begin(), tracks_.end(), [&](const Track& t) { - return t.getAttribute().number == track1; - }); - if (it1 == tracks_.end()) throw std::invalid_argument("Invalid track number"); - auto it2 = std::find_if(tracks_.begin(), tracks_.end(), [&](const Track& t) { - return t.getAttribute().number == track2; - }); - if (it2 == tracks_.end()) throw std::invalid_argument("Invalid track number"); - - TrackAttribute attrib1 = it1->getAttribute(); - TrackAttribute attrib2 = it2->getAttribute(); - it1->setAttribute(attrib2.number, attrib2.source, attrib2.channelInSource); - it2->setAttribute(attrib1.number, attrib1.source, attrib1.channelInSource); - - std::iter_swap(it1, it2); -} - -Bookmark::Bookmark(std::string argname, int argorder, int argstep) - : name(argname), order(argorder), step(argstep) -{ -} diff -Nru bambootracker-0.4.5+git20121209/BambooTracker/module/song.hpp bambootracker-0.4.6/BambooTracker/module/song.hpp --- bambootracker-0.4.5+git20121209/BambooTracker/module/song.hpp 2020-12-08 14:05:28.000000000 +0000 +++ bambootracker-0.4.6/BambooTracker/module/song.hpp 2021-02-11 14:37:17.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (C) 2018-2020 Rerrah + * Copyright (C) 2018-2021 Rerrah * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation @@ -26,35 +26,52 @@ #pragma once #include -#include +#include #include #include #include "track.hpp" -#include "misc.hpp" -struct SongStyle; -struct Bookmark; +enum class SongType +{ + Standard, + FM3chExpanded +}; + +struct SongStyle +{ + SongType type; + std::vector trackAttribs; // Always sorted by number +}; + +struct Bookmark +{ + std::string name = u8""; + int order, step; + + Bookmark(const std::string& argname, int argorder, int argstep); +}; class Song { public: - Song(int number, SongType songType = SongType::Standard, std::string title = u8"", bool isUsedTempo = true, - int tempo = 150, int groove = 0, int speed = 6, size_t defaultPatternSize = 64); - - void setNumber(int n); - int getNumber() const; - void setTitle(std::string title); - std::string getTitle() const; - void setTempo(int tempo); - int getTempo() const; - void setGroove(int groove); - int getGroove() const; - void toggleTempoOrGroove(bool isUsedTempo); - bool isUsedTempo() const; - void setSpeed(int speed); - int getSpeed() const; + Song(int number, SongType songType = SongType::Standard, const std::string& title = u8"", + bool isUsedTempo = true, int tempo = 150, int groove = 0, int speed = 6, + size_t defaultPatternSize = 64); + + inline void setNumber(int n) noexcept { num_ = n; } + inline int getNumber() const noexcept { return num_; } + inline void setTitle(const std::string& title) { title_ = title; } + inline std::string getTitle() const noexcept { return title_; } + inline void setTempo(int tempo) noexcept { tempo_ = tempo; } + inline int getTempo() const noexcept { return tempo_; } + inline void setGroove(int groove) noexcept { groove_ = groove; } + inline int getGroove() const noexcept { return groove_; } + inline void toggleTempoOrGroove(bool isUsedTempo) noexcept { isUsedTempo_ = isUsedTempo; } + inline bool isUsedTempo() const noexcept { return isUsedTempo_; } + inline void setSpeed(int speed) noexcept { speed_ = speed; } + inline int getSpeed() const noexcept { return speed_; } void setDefaultPatternSize(size_t size); - size_t getDefaultPatternSize() const; + inline size_t getDefaultPatternSize() const noexcept { return defPtnSize_; } size_t getPatternSizeFromOrderNumber(int order); SongStyle getStyle() const; @@ -62,20 +79,24 @@ Track& getTrack(int num); void changeType(SongType type); - std::vector getOrderData(int order); + std::vector getOrderData(int order) const; size_t getOrderSize() const; bool canAddNewOrder() const; void insertOrderBelow(int order); void deleteOrder(int order); void swapOrder(int a, int b); - std::unordered_set getRegisteredInstruments() const; + std::set getRegisteredInstruments() const; void clearUnusedPatterns(); - void replaceDuplicateInstrumentsInPatterns(std::unordered_map map); + void replaceDuplicateInstrumentsInPatterns(const std::unordered_map& map); - int addBookmark(std::string name, int order, int step); - void changeBookmark(int i, std::string name, int order, int step); + void transpose(int seminotes, const std::vector& excludeInsts); + void swapTracks(int track1, int track2); + + // Bookmark + int addBookmark(const std::string& name, int order, int step); + void changeBookmark(int i, const std::string& name, int order, int step); void removeBookmark(int i); void clearBookmark(); void swapBookmarks(int a, int b); @@ -83,12 +104,18 @@ void sortBookmarkByName(); Bookmark getBookmark(int i) const; std::vector findBookmarks(int order, int step) const; - Bookmark getPreviousBookmark(int order, int step); - Bookmark getNextBookmark(int order, int step); + Bookmark getPreviousBookmark(int order, int step) const; + Bookmark getNextBookmark(int order, int step) const; size_t getBookmarkSize() const; - void transpose(int seminotes, std::vector excludeInsts); - void swapTracks(int track1, int track2); + inline static size_t getFMChannelCount(SongType type) + { + switch (type) { + case SongType::Standard: return 6; + case SongType::FM3chExpanded: return 9; + default: throw std::invalid_argument("Invalid SongType."); + } + } private: int num_; @@ -105,17 +132,3 @@ std::vector getSortedBookmarkList() const; }; - -struct SongStyle -{ - SongType type; - std::vector trackAttribs; // Always sorted by number -}; - -struct Bookmark -{ - std::string name = u8""; - int order, step; - - Bookmark(std::string argname, int argorder, int argstep); -}; diff -Nru bambootracker-0.4.5+git20121209/BambooTracker/module/step.cpp bambootracker-0.4.6/BambooTracker/module/step.cpp --- bambootracker-0.4.5+git20121209/BambooTracker/module/step.cpp 2020-12-08 14:05:28.000000000 +0000 +++ bambootracker-0.4.6/BambooTracker/module/step.cpp 2021-02-11 14:37:17.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (C) 2018 Rerrah + * Copyright (C) 2018-2020 Rerrah * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation @@ -25,83 +25,37 @@ #include "step.hpp" +const std::string Step::EFF_ID_NONE = "--"; + Step::Step() - : noteNum_(-1), - instNum_(-1), - vol_(-1) -{ - for (size_t i = 0; i < 4; ++i) { - effID_[i] = "--"; - effVal_[i] = -1; + : note_(NOTE_NONE), + inst_(INST_NONE), + vol_(VOLUME_NONE) +{ + for (size_t i = 0; i < N_EFFECT; ++i) { + eff_[i].id = EFF_ID_NONE; + eff_[i].value = EFF_VAL_NONE; } } -int Step::getNoteNumber() const -{ - return noteNum_; -} - -void Step::setNoteNumber(int num) -{ - noteNum_ = num; -} - -int Step::getInstrumentNumber() const -{ - return instNum_; -} - -void Step::setInstrumentNumber(int num) -{ - instNum_ = num; -} - -int Step::getVolume() const -{ - return vol_; -} - -void Step::setVolume(int volume) -{ - vol_ = volume; -} - -std::string Step::getEffectID(int n) const -{ - return effID_[n]; -} - -void Step::setEffectID(int n, std::string str) -{ - effID_[n] = str; -} - -int Step::getEffectValue(int n) const -{ - return effVal_[n]; -} - -void Step::setEffectValue(int n, int v) -{ - effVal_[n] = v; -} - -int Step::checkEffectID(std::string str) const +void Step::clear() { - for (int i = 0; i < 4; ++i) { - if (effID_[i] == str && effVal_[i] != -1) return i; + clearNoteNumber(); + clearInstrumentNumber(); + clearVolume(); + for (size_t i = 0; i < N_EFFECT; ++i) { + clearEffect(i); } - return -1; } -bool Step::existCommand() const +bool Step::hasEvent() const { - if (noteNum_ != -1) return true; - if (instNum_ != -1) return true; - if (vol_ != -1) return true; - for (int i = 0; i < 4; ++i) { - if (effID_[i] != "--") return true; - if (effVal_[i] != -1) return true; + if (!isEmptyNote()) return true; + if (hasInstrument()) return true; + if (hasVolume()) return true; + for (int i = 0; i < N_EFFECT; ++i) { + if (hasEffectId(i)) return true; + if (hasEffectValue(i)) return true; } return false; } diff -Nru bambootracker-0.4.5+git20121209/BambooTracker/module/step.hpp bambootracker-0.4.6/BambooTracker/module/step.hpp --- bambootracker-0.4.5+git20121209/BambooTracker/module/step.hpp 2020-12-08 14:05:28.000000000 +0000 +++ bambootracker-0.4.6/BambooTracker/module/step.hpp 2021-02-11 14:37:17.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (C) 2018-2019 Rerrah + * Copyright (C) 2018-2020 Rerrah * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation @@ -26,31 +26,93 @@ #pragma once #include +#include class Step { public: Step(); - int getNoteNumber() const; - void setNoteNumber(int num); + enum NoteValue : int + { + NOTE_NONE = -1, + // Special notes + NOTE_KEY_OFF = -2, + NOTE_ECHO0 = -3, + NOTE_ECHO1 = -4, + NOTE_ECHO2 = -5, + NOTE_ECHO3 = -6 + }; + inline int getNoteNumber() const noexcept { return note_; } + inline void setNoteNumber(int num) { note_ = num; } + inline void setKeyOff() { note_ = NOTE_KEY_OFF; } + inline void setEchoBuffer(int n) { note_ = NOTE_ECHO0 - n; } + inline void clearNoteNumber() noexcept { note_ = NOTE_NONE; } + inline bool hasGeneralNote() const noexcept { return note_ > NOTE_NONE; } + inline bool hasKeyOff() const noexcept { return note_ == NOTE_KEY_OFF; } + inline bool hasNoteEchoBuffer(int n) const { return note_ == (NOTE_ECHO0 - n); } + inline bool isEmptyNote() const noexcept { return note_ == NOTE_NONE; } + + static inline bool testEmptyNote(int note) { return note == NOTE_NONE; } + + static constexpr int INST_NONE = -1; + inline int getInstrumentNumber() const noexcept { return inst_; } + inline void setInstrumentNumber(int num) { inst_ = num; } + inline void clearInstrumentNumber() noexcept { inst_ = INST_NONE; } + inline bool hasInstrument() const noexcept { return inst_ != INST_NONE; } + + static inline bool testEmptyInstrument(int inst) { return inst == INST_NONE; } + + static constexpr int VOLUME_NONE = -1; + inline int getVolume() const noexcept { return vol_; } + inline void setVolume(int volume) { vol_ = volume; } + inline void clearVolume() noexcept { vol_ = VOLUME_NONE; } + inline bool hasVolume() const noexcept { return vol_ != VOLUME_NONE; } + + static inline bool testEmptyVolume(int vol) { return vol == VOLUME_NONE; } + + static const std::string EFF_ID_NONE; // "--" + inline std::string getEffectId(int n) const { return eff_[n].id; } + inline void setEffectId(int n, const std::string& str) { eff_[n].id = str; } + inline void clearEffectId(int n) { eff_[n].id = EFF_ID_NONE; } + inline bool hasEffectId(int n) const { return eff_[n].id != EFF_ID_NONE; } + + static inline bool testEmptyEffectId(const std::string& id) { return id == EFF_ID_NONE; } + + static constexpr int EFF_VAL_NONE = -1; + inline int getEffectValue(int n) const { return eff_[n].value; } + inline void setEffectValue(int n, int v) { eff_[n].value = v; } + inline void clearEffectValue(int n) { eff_[n].value = EFF_VAL_NONE; } + inline bool hasEffectValue(int n) const { return eff_[n].value != EFF_VAL_NONE; } + + static inline bool testEmptyEffectValue(int v) { return v == EFF_VAL_NONE; } + + struct PlainEffect + { + std::string id; + int value; + }; + + static constexpr int N_EFFECT = 4; + + inline PlainEffect getEffect(int n) const { return eff_[n]; } + inline void setEffect(int n, const PlainEffect& effect) { eff_[n] = effect; } + inline void setEffect(int n, const std::string& id, int value) + { + setEffectId(n, id); + setEffectValue(n, value); + } + inline void clearEffect(int n) + { + clearEffectId(n); + clearEffectValue(n); + } - int getInstrumentNumber() const; - void setInstrumentNumber(int num); + static constexpr int N_COLUMN = 3 + N_EFFECT * 2; - int getVolume() const; - void setVolume(int volume); + void clear(); - std::string getEffectID(int n) const; - void setEffectID(int n, std::string str); - - int getEffectValue(int n) const; - void setEffectValue(int n, int v); - - /// NOTE: Deprecated - int checkEffectID(std::string str) const; - - bool existCommand() const; + bool hasEvent() const; private: /// noteNum_ @@ -61,18 +123,21 @@ /// -4: echo 2 notes before /// -5: echo 3 notes before /// -6: echo 4 notes before - int noteNum_; + int note_; /// instNum_ /// 0<=: instrument number /// -1: none - int instNum_; + int inst_; /// vol_ /// 0<=: volume level /// -1: none int vol_; - std::string effID_[4]; - /// effVal_ + /// eff + /// [id] + /// "--": none + /// other: effect id + /// [value] /// 0<=: effect value /// -1: none - int effVal_[4]; + PlainEffect eff_[N_EFFECT]; }; diff -Nru bambootracker-0.4.5+git20121209/BambooTracker/module/track.cpp bambootracker-0.4.6/BambooTracker/module/track.cpp --- bambootracker-0.4.5+git20121209/BambooTracker/module/track.cpp 2020-12-08 14:05:28.000000000 +0000 +++ bambootracker-0.4.6/BambooTracker/module/track.cpp 2021-02-11 14:37:17.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (C) 2018-2020 Rerrah + * Copyright (C) 2018-2021 Rerrah * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation @@ -25,81 +25,54 @@ #include "track.hpp" #include +#include +#include "utils.hpp" + +namespace +{ +constexpr int PATTERN_SIZE = 256; +constexpr int MAX_ORDER_SIZE = 256; +} Track::Track(int number, SoundSource source, int channelInSource, int defPattenSize) - : attrib_(std::make_unique()), - effetDisplayWidth_(0) + : effetDisplayWidth_(0) { setAttribute(number, source, channelInSource); - for (int i = 0; i < 256; ++i) { + patterns_.reserve(PATTERN_SIZE); + for (int i = 0; i < PATTERN_SIZE; ++i) { patterns_.emplace_back(i, defPattenSize); } - patterns_[0].usedCountUp(); + patterns_[0].increaseUsedCount(); order_.push_back(0); // Set first order } -Track::Track(const Track& other) - : attrib_(std::make_unique()) -{ - setAttribute(other.attrib_->number, other.attrib_->source, other.attrib_->channelInSource); - order_ = other.order_; - patterns_ = other.patterns_; - effetDisplayWidth_ = other.effetDisplayWidth_; -} - -Track& Track::operator=(const Track& other) -{ - setAttribute(other.attrib_->number, other.attrib_->source, other.attrib_->channelInSource); - order_ = other.order_; - patterns_ = other.patterns_; - effetDisplayWidth_ = other.effetDisplayWidth_; - return *this; -} - -Track::Track(Track&& other) noexcept -{ - attrib_ = std::move(other.attrib_); - order_ = std::move(other.order_); - patterns_ = std::move(other.patterns_); - effetDisplayWidth_ = std::move(other.effetDisplayWidth_); -} - -Track& Track::operator=(Track&& other) noexcept -{ - attrib_ = std::move(other.attrib_); - order_ = std::move(other.order_); - patterns_ = std::move(other.patterns_); - effetDisplayWidth_ = std::move(other.effetDisplayWidth_); - return *this; -} - -void Track::setAttribute(int number, SoundSource source, int channelInSource) +void Track::setAttribute(int number, SoundSource source, int channelInSource) noexcept { - attrib_->number = number; - attrib_->source = source; - attrib_->channelInSource = channelInSource; + attrib_.number = number; + attrib_.source = source; + attrib_.channelInSource = channelInSource; } -TrackAttribute Track::getAttribute() const +OrderInfo Track::getOrderInfo(int order) const { - return *attrib_; + OrderInfo info; + info.trackAttribute = attrib_; + info.order = order; + info.patten = order_.at(static_cast(order)); + return info; } -OrderData Track::getOrderData(int order) +size_t Track::getOrderSize() const { - OrderData res; - res.trackAttribute = getAttribute(); - res.order = order; - res.patten = order_.at(static_cast(order)); - return res; + return order_.size(); } -size_t Track::getOrderSize() const +bool Track::canAddNewOrder() const { - return order_.size(); + return order_.size() < MAX_ORDER_SIZE; } Pattern& Track::getPattern(int num) @@ -114,11 +87,10 @@ int Track::searchFirstUneditedUnusedPattern() const { - for (size_t i = 0; i < patterns_.size(); ++i) { - if (!patterns_[i].existCommand() && !patterns_[i].getUsedCount()) - return static_cast(i); - } - return -1; + auto it = utils::findIf(patterns_, [](const Pattern& pattern) { + return (!pattern.hasEvent() && !pattern.getUsedCount()); + }); + return (it == patterns_.cend() ? -1 : std::distance(patterns_.cbegin(), it)); } int Track::clonePattern(int num) @@ -133,44 +105,39 @@ std::vector Track::getEditedPatternIndices() const { - std::vector list; - for (size_t i = 0; i < 256; ++i) { - if (patterns_[i].existCommand()) list.push_back(static_cast(i)); - } - return list; + return utils::findIndicesIf(patterns_, [](const Pattern& pattern) { return pattern.hasEvent(); }); } -std::unordered_set Track::getRegisteredInstruments() const +std::set Track::getRegisteredInstruments() const { - std::unordered_set set; - for (auto& pattern : patterns_) { - for (auto& n : pattern.getRegisteredInstruments()) { - set.insert(n); - } + std::set set; + for (const Pattern& pattern : patterns_) { + auto&& insts = pattern.getRegisteredInstruments(); + std::copy(insts.cbegin(), insts.cend(), std::inserter(set, set.end())); } return set; } void Track::registerPatternToOrder(int order, int pattern) { - patterns_.at(static_cast(pattern)).usedCountUp(); - patterns_.at(static_cast(order_.at(static_cast(order)))).usedCountDown(); + patterns_.at(static_cast(pattern)).increaseUsedCount(); + patterns_.at(static_cast(order_.at(static_cast(order)))).decreaseUsedCount(); order_.at(static_cast(order)) = pattern; } void Track::insertOrderBelow(int order) { int n = searchFirstUneditedUnusedPattern(); - if (n == -1) n = 255; + if (n == -1) n = PATTERN_SIZE - 1; if (order == static_cast(order_.size()) - 1) order_.push_back(n); else order_.insert(order_.begin() + order + 1, n); - patterns_[static_cast(n)].usedCountUp(); + patterns_[static_cast(n)].increaseUsedCount(); } void Track::deleteOrder(int order) { - patterns_.at(static_cast(order_.at(static_cast(order)))).usedCountDown(); + patterns_.at(static_cast(order_.at(static_cast(order)))).decreaseUsedCount(); order_.erase(order_.begin() + order); } @@ -181,45 +148,31 @@ void Track::changeDefaultPatternSize(size_t size) { - for (auto& ptn : patterns_) { - ptn.changeSize(size); - } -} - -void Track::setEffectDisplayWidth(size_t w) -{ - effetDisplayWidth_ = w; -} - -size_t Track::getEffectDisplayWidth() const -{ - return effetDisplayWidth_; + for (auto& ptn : patterns_) ptn.changeSize(size); } void Track::clearUnusedPatterns() { - for (size_t i = 0; i < 256; ++i) { - if (!patterns_[i].getUsedCount() && patterns_[i].existCommand()) - patterns_[i].clear(); + for (Pattern& pattern : patterns_) { + if (!pattern.getUsedCount() && pattern.hasEvent()) + pattern.clear(); } } -void Track::replaceDuplicateInstrumentsInPatterns(std::unordered_map map) +void Track::replaceDuplicateInstrumentsInPatterns(const std::unordered_map& map) { - for (size_t i = 0; i < 256; ++i) { - Pattern& pattern = patterns_[i]; - if (pattern.existCommand()) { + for (Pattern& pattern : patterns_) { + if (pattern.hasEvent()) { for (size_t i = 0; i < pattern.getSize(); ++i) { Step& step = pattern.getStep(static_cast(i)); int inst = step.getInstrumentNumber(); - if (map.count(inst)) step.setInstrumentNumber(map[inst]); + if (map.count(inst)) step.setInstrumentNumber(map.at(inst)); } } - } } -void Track::transpose(int seminotes, std::vector excludeInsts) +void Track::transpose(int seminotes, const std::vector& excludeInsts) { - for (auto& pattern : patterns_) pattern.transpose(seminotes, excludeInsts); + for (Pattern& pattern : patterns_) pattern.transpose(seminotes, excludeInsts); } diff -Nru bambootracker-0.4.5+git20121209/BambooTracker/module/track.hpp bambootracker-0.4.6/BambooTracker/module/track.hpp --- bambootracker-0.4.5+git20121209/BambooTracker/module/track.hpp 2020-12-08 14:05:28.000000000 +0000 +++ bambootracker-0.4.6/BambooTracker/module/track.hpp 2021-02-11 14:37:17.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (C) 2018-2020 Rerrah + * Copyright (C) 2018-2021 Rerrah * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation @@ -26,34 +26,41 @@ #pragma once #include -#include -#include +#include #include #include "pattern.hpp" -#include "misc.hpp" +#include "bamboo_tracker_defs.hpp" -struct TrackAttribute; -struct OrderData; +struct TrackAttribute +{ + int number; + SoundSource source; + int channelInSource; +}; + +struct OrderInfo +{ + TrackAttribute trackAttribute; + int order; + int patten; +}; class Track { public: Track(int number, SoundSource source, int channelInSource, int defPattenSize); - Track(const Track& other); - Track& operator=(const Track& other); - Track(Track&& other) noexcept; - Track& operator=(Track&& other) noexcept; - - void setAttribute(int number, SoundSource source, int channelInSource); - TrackAttribute getAttribute() const; - OrderData getOrderData(int order); + + void setAttribute(int number, SoundSource source, int channelInSource) noexcept; + inline TrackAttribute getAttribute() const noexcept { return attrib_; } + OrderInfo getOrderInfo(int order) const; size_t getOrderSize() const; + bool canAddNewOrder() const; Pattern& getPattern(int num); Pattern& getPatternFromOrderNumber(int num); int searchFirstUneditedUnusedPattern() const; int clonePattern(int num); std::vector getEditedPatternIndices() const; - std::unordered_set getRegisteredInstruments() const; + std::set getRegisteredInstruments() const; void registerPatternToOrder(int order, int pattern); void insertOrderBelow(int order); @@ -62,33 +69,18 @@ void changeDefaultPatternSize(size_t size); - void setEffectDisplayWidth(size_t w); - size_t getEffectDisplayWidth() const; + inline void setEffectDisplayWidth(size_t w) noexcept { effetDisplayWidth_ = w; } + inline size_t getEffectDisplayWidth() const noexcept { return effetDisplayWidth_; } void clearUnusedPatterns(); - void replaceDuplicateInstrumentsInPatterns(std::unordered_map map); + void replaceDuplicateInstrumentsInPatterns(const std::unordered_map& map); - void transpose(int seminotes, std::vector excludeInsts); + void transpose(int seminotes, const std::vector& excludeInsts); private: - std::unique_ptr attrib_; + TrackAttribute attrib_; std::vector order_; std::vector patterns_; size_t effetDisplayWidth_; }; - -struct TrackAttribute -{ - int number; - SoundSource source; - int channelInSource; -}; - - -struct OrderData -{ - TrackAttribute trackAttribute; - int order; - int patten; -}; diff -Nru bambootracker-0.4.5+git20121209/BambooTracker/note.cpp bambootracker-0.4.6/BambooTracker/note.cpp --- bambootracker-0.4.5+git20121209/BambooTracker/note.cpp 1970-01-01 00:00:00.000000000 +0000 +++ bambootracker-0.4.6/BambooTracker/note.cpp 2021-02-11 14:37:17.000000000 +0000 @@ -0,0 +1,1147 @@ +/* + * Copyright (C) 2018-2021 Rerrah + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +#include "note.hpp" +#include +#include + +namespace note_utils +{ +namespace +{ +const uint16_t centTableFM[3072] = { + 0x026a, 0x026b, 0x026c, 0x026e, 0x026f, 0x0270, 0x0271, 0x0272, 0x0273, 0x0274, 0x0276, 0x0277, + 0x0278, 0x0279, 0x027a, 0x027b, 0x027c, 0x027e, 0x027f, 0x0280, 0x0281, 0x0282, 0x0283, 0x0284, + 0x0286, 0x0287, 0x0288, 0x0289, 0x028a, 0x028b, 0x028d, 0x028e, 0x028f, 0x0290, 0x0291, 0x0293, + 0x0294, 0x0295, 0x0296, 0x0297, 0x0299, 0x029a, 0x029b, 0x029c, 0x029d, 0x029f, 0x02a0, 0x02a1, + 0x02a2, 0x02a3, 0x02a5, 0x02a6, 0x02a7, 0x02a8, 0x02aa, 0x02ab, 0x02ac, 0x02ad, 0x02ae, 0x02b0, + 0x02b1, 0x02b2, 0x02b3, 0x02b5, 0x02b6, 0x02b7, 0x02b8, 0x02ba, 0x02bb, 0x02bc, 0x02be, 0x02bf, + 0x02c0, 0x02c1, 0x02c3, 0x02c4, 0x02c5, 0x02c6, 0x02c8, 0x02c9, 0x02ca, 0x02cc, 0x02cd, 0x02ce, + 0x02cf, 0x02d1, 0x02d2, 0x02d3, 0x02d5, 0x02d6, 0x02d7, 0x02d9, 0x02da, 0x02db, 0x02dd, 0x02de, + 0x02df, 0x02e1, 0x02e2, 0x02e3, 0x02e5, 0x02e6, 0x02e7, 0x02e9, 0x02ea, 0x02eb, 0x02ed, 0x02ee, + 0x02ef, 0x02f1, 0x02f2, 0x02f3, 0x02f5, 0x02f6, 0x02f7, 0x02f9, 0x02fa, 0x02fc, 0x02fd, 0x02fe, + 0x0300, 0x0301, 0x0303, 0x0304, 0x0305, 0x0307, 0x0308, 0x030a, 0x030b, 0x030c, 0x030e, 0x030f, + 0x0311, 0x0312, 0x0313, 0x0315, 0x0316, 0x0318, 0x0319, 0x031b, 0x031c, 0x031d, 0x031f, 0x0320, + 0x0322, 0x0323, 0x0325, 0x0326, 0x0328, 0x0329, 0x032a, 0x032c, 0x032d, 0x032f, 0x0330, 0x0332, + 0x0333, 0x0335, 0x0336, 0x0338, 0x0339, 0x033b, 0x033c, 0x033e, 0x033f, 0x0341, 0x0342, 0x0344, + 0x0345, 0x0347, 0x0348, 0x034a, 0x034b, 0x034d, 0x034e, 0x0350, 0x0351, 0x0353, 0x0355, 0x0356, + 0x0358, 0x0359, 0x035b, 0x035c, 0x035e, 0x035f, 0x0361, 0x0362, 0x0364, 0x0366, 0x0367, 0x0369, + 0x036a, 0x036c, 0x036d, 0x036f, 0x0371, 0x0372, 0x0374, 0x0375, 0x0377, 0x0379, 0x037a, 0x037c, + 0x037d, 0x037f, 0x0381, 0x0382, 0x0384, 0x0386, 0x0387, 0x0389, 0x038a, 0x038c, 0x038e, 0x038f, + 0x0391, 0x0393, 0x0394, 0x0396, 0x0398, 0x0399, 0x039b, 0x039d, 0x039e, 0x03a0, 0x03a2, 0x03a3, + 0x03a5, 0x03a7, 0x03a8, 0x03aa, 0x03ac, 0x03ad, 0x03af, 0x03b1, 0x03b3, 0x03b4, 0x03b6, 0x03b8, + 0x03b9, 0x03bb, 0x03bd, 0x03bf, 0x03c0, 0x03c2, 0x03c4, 0x03c6, 0x03c7, 0x03c9, 0x03cb, 0x03cd, + 0x03ce, 0x03d0, 0x03d2, 0x03d4, 0x03d5, 0x03d7, 0x03d9, 0x03db, 0x03dd, 0x03de, 0x03e0, 0x03e2, + 0x03e4, 0x03e5, 0x03e7, 0x03e9, 0x03eb, 0x03ed, 0x03ef, 0x03f0, 0x03f2, 0x03f4, 0x03f6, 0x03f8, + 0x03f9, 0x03fb, 0x03fd, 0x03ff, 0x0401, 0x0403, 0x0405, 0x0406, 0x0408, 0x040a, 0x040c, 0x040e, + 0x0410, 0x0412, 0x0414, 0x0415, 0x0417, 0x0419, 0x041b, 0x041d, 0x041f, 0x0421, 0x0423, 0x0425, + 0x0427, 0x0428, 0x042a, 0x042c, 0x042e, 0x0430, 0x0432, 0x0434, 0x0436, 0x0438, 0x043a, 0x043c, + 0x043e, 0x0440, 0x0442, 0x0444, 0x0446, 0x0448, 0x044a, 0x044c, 0x044e, 0x0450, 0x0452, 0x0454, + 0x0456, 0x0458, 0x045a, 0x045c, 0x045e, 0x0460, 0x0462, 0x0464, 0x0466, 0x0468, 0x046a, 0x046c, + 0x046e, 0x0470, 0x0472, 0x0474, 0x0476, 0x0478, 0x047a, 0x047c, 0x047e, 0x0480, 0x0483, 0x0485, + 0x0487, 0x0489, 0x048b, 0x048d, 0x048f, 0x0491, 0x0493, 0x0495, 0x0498, 0x049a, 0x049c, 0x049e, + 0x04a0, 0x04a2, 0x04a4, 0x04a6, 0x04a9, 0x04ab, 0x04ad, 0x04af, 0x04b1, 0x04b3, 0x04b6, 0x04b8, + 0x04ba, 0x04bc, 0x04be, 0x04c1, 0x04c3, 0x04c5, 0x04c7, 0x04c9, 0x04cc, 0x04ce, 0x04d0, 0x04d2, + 0x04d4, 0x04d7, 0x04d9, 0x04db, 0x04dd, 0x04e0, 0x04e2, 0x04e4, 0x04e6, 0x04e9, 0x04eb, 0x04ed, + 0x04f0, 0x04f2, 0x04f4, 0x04f6, 0x04f9, 0x04fb, 0x04fd, 0x0500, 0x0502, 0x0504, 0x0507, 0x0509, + 0x050b, 0x050e, 0x0510, 0x0512, 0x0515, 0x0517, 0x0519, 0x051c, 0x051e, 0x0520, 0x0523, 0x0525, + 0x0528, 0x052a, 0x052c, 0x052f, 0x0531, 0x0533, 0x0536, 0x0538, 0x053b, 0x053d, 0x0540, 0x0542, + 0x0544, 0x0547, 0x0549, 0x054c, 0x054e, 0x0551, 0x0553, 0x0556, 0x0558, 0x055a, 0x055d, 0x055f, + 0x0562, 0x0564, 0x0567, 0x0569, 0x056c, 0x056e, 0x0571, 0x0573, 0x0576, 0x0578, 0x057b, 0x057e, + 0x0580, 0x0583, 0x0585, 0x0588, 0x058a, 0x058d, 0x058f, 0x0592, 0x0595, 0x0597, 0x059a, 0x059c, + 0x059f, 0x05a2, 0x05a4, 0x05a7, 0x05a9, 0x05ac, 0x05af, 0x05b1, 0x05b4, 0x05b6, 0x05b9, 0x05bc, + 0x05be, 0x05c1, 0x05c4, 0x05c6, 0x05c9, 0x05cc, 0x05ce, 0x05d1, 0x05d4, 0x05d7, 0x05d9, 0x05dc, + 0x05df, 0x05e1, 0x05e4, 0x05e7, 0x05ea, 0x05ec, 0x05ef, 0x05f2, 0x05f4, 0x05f7, 0x05fa, 0x05fd, + 0x0600, 0x0602, 0x0605, 0x0608, 0x060b, 0x060d, 0x0610, 0x0613, 0x0616, 0x0619, 0x061c, 0x061e, + 0x0621, 0x0624, 0x0627, 0x062a, 0x062d, 0x062f, 0x0632, 0x0635, 0x0638, 0x063b, 0x063e, 0x0641, + 0x0644, 0x0646, 0x0649, 0x064c, 0x064f, 0x0652, 0x0655, 0x0658, 0x065b, 0x065e, 0x0661, 0x0664, + 0x0667, 0x066a, 0x066d, 0x0670, 0x0673, 0x0675, 0x0678, 0x067b, 0x067e, 0x0681, 0x0684, 0x0687, + 0x068b, 0x068e, 0x0691, 0x0694, 0x0697, 0x069a, 0x069d, 0x06a0, 0x06a3, 0x06a6, 0x06a9, 0x06ac, + 0x06af, 0x06b2, 0x06b5, 0x06b8, 0x06bc, 0x06bf, 0x06c2, 0x06c5, 0x06c8, 0x06cb, 0x06ce, 0x06d1, + 0x06d5, 0x06d8, 0x06db, 0x06de, 0x06e1, 0x06e5, 0x06e8, 0x06eb, 0x06ee, 0x06f1, 0x06f5, 0x06f8, + 0x06fb, 0x06fe, 0x0701, 0x0705, 0x0708, 0x070b, 0x070e, 0x0712, 0x0715, 0x0718, 0x071b, 0x071f, + 0x0722, 0x0725, 0x0729, 0x072c, 0x072f, 0x0733, 0x0736, 0x0739, 0x073d, 0x0740, 0x0743, 0x0747, + 0x074a, 0x074d, 0x0751, 0x0754, 0x0758, 0x075b, 0x075e, 0x0762, 0x0765, 0x0769, 0x076c, 0x076f, + 0x0773, 0x0776, 0x077a, 0x077d, 0x0781, 0x0784, 0x0788, 0x078b, 0x078f, 0x0792, 0x0796, 0x0799, + 0x079d, 0x07a0, 0x07a4, 0x07a7, 0x07ab, 0x07ae, 0x07b2, 0x07b5, 0x07b9, 0x07bd, 0x07c0, 0x07c4, + 0x07c7, 0x07cb, 0x07cf, 0x07d2, 0x07d6, 0x07d9, 0x07dd, 0x07e1, 0x07e4, 0x07e8, 0x07ec, 0x07ef, + 0x07f3, 0x07f7, 0x07fa, 0x07fe, 0x0c01, 0x0c03, 0x0c05, 0x0c06, 0x0c08, 0x0c0a, 0x0c0c, 0x0c0e, + 0x0c10, 0x0c12, 0x0c14, 0x0c15, 0x0c17, 0x0c19, 0x0c1b, 0x0c1d, 0x0c1f, 0x0c21, 0x0c23, 0x0c25, + 0x0c27, 0x0c28, 0x0c2a, 0x0c2c, 0x0c2e, 0x0c30, 0x0c32, 0x0c34, 0x0c36, 0x0c38, 0x0c3a, 0x0c3c, + 0x0c3e, 0x0c40, 0x0c42, 0x0c44, 0x0c46, 0x0c48, 0x0c4a, 0x0c4c, 0x0c4e, 0x0c50, 0x0c52, 0x0c54, + 0x0c56, 0x0c58, 0x0c5a, 0x0c5c, 0x0c5e, 0x0c60, 0x0c62, 0x0c64, 0x0c66, 0x0c68, 0x0c6a, 0x0c6c, + 0x0c6e, 0x0c70, 0x0c72, 0x0c74, 0x0c76, 0x0c78, 0x0c7a, 0x0c7c, 0x0c7e, 0x0c80, 0x0c83, 0x0c85, + 0x0c87, 0x0c89, 0x0c8b, 0x0c8d, 0x0c8f, 0x0c91, 0x0c93, 0x0c95, 0x0c98, 0x0c9a, 0x0c9c, 0x0c9e, + 0x0ca0, 0x0ca2, 0x0ca4, 0x0ca6, 0x0ca9, 0x0cab, 0x0cad, 0x0caf, 0x0cb1, 0x0cb3, 0x0cb6, 0x0cb8, + 0x0cba, 0x0cbc, 0x0cbe, 0x0cc1, 0x0cc3, 0x0cc5, 0x0cc7, 0x0cc9, 0x0ccc, 0x0cce, 0x0cd0, 0x0cd2, + 0x0cd4, 0x0cd7, 0x0cd9, 0x0cdb, 0x0cdd, 0x0ce0, 0x0ce2, 0x0ce4, 0x0ce6, 0x0ce9, 0x0ceb, 0x0ced, + 0x0cf0, 0x0cf2, 0x0cf4, 0x0cf6, 0x0cf9, 0x0cfb, 0x0cfd, 0x0d00, 0x0d02, 0x0d04, 0x0d07, 0x0d09, + 0x0d0b, 0x0d0e, 0x0d10, 0x0d12, 0x0d15, 0x0d17, 0x0d19, 0x0d1c, 0x0d1e, 0x0d20, 0x0d23, 0x0d25, + 0x0d28, 0x0d2a, 0x0d2c, 0x0d2f, 0x0d31, 0x0d33, 0x0d36, 0x0d38, 0x0d3b, 0x0d3d, 0x0d40, 0x0d42, + 0x0d44, 0x0d47, 0x0d49, 0x0d4c, 0x0d4e, 0x0d51, 0x0d53, 0x0d56, 0x0d58, 0x0d5a, 0x0d5d, 0x0d5f, + 0x0d62, 0x0d64, 0x0d67, 0x0d69, 0x0d6c, 0x0d6e, 0x0d71, 0x0d73, 0x0d76, 0x0d78, 0x0d7b, 0x0d7e, + 0x0d80, 0x0d83, 0x0d85, 0x0d88, 0x0d8a, 0x0d8d, 0x0d8f, 0x0d92, 0x0d95, 0x0d97, 0x0d9a, 0x0d9c, + 0x0d9f, 0x0da2, 0x0da4, 0x0da7, 0x0da9, 0x0dac, 0x0daf, 0x0db1, 0x0db4, 0x0db6, 0x0db9, 0x0dbc, + 0x0dbe, 0x0dc1, 0x0dc4, 0x0dc6, 0x0dc9, 0x0dcc, 0x0dce, 0x0dd1, 0x0dd4, 0x0dd7, 0x0dd9, 0x0ddc, + 0x0ddf, 0x0de1, 0x0de4, 0x0de7, 0x0dea, 0x0dec, 0x0def, 0x0df2, 0x0df4, 0x0df7, 0x0dfa, 0x0dfd, + 0x0e00, 0x0e02, 0x0e05, 0x0e08, 0x0e0b, 0x0e0d, 0x0e10, 0x0e13, 0x0e16, 0x0e19, 0x0e1c, 0x0e1e, + 0x0e21, 0x0e24, 0x0e27, 0x0e2a, 0x0e2d, 0x0e2f, 0x0e32, 0x0e35, 0x0e38, 0x0e3b, 0x0e3e, 0x0e41, + 0x0e44, 0x0e46, 0x0e49, 0x0e4c, 0x0e4f, 0x0e52, 0x0e55, 0x0e58, 0x0e5b, 0x0e5e, 0x0e61, 0x0e64, + 0x0e67, 0x0e6a, 0x0e6d, 0x0e70, 0x0e73, 0x0e75, 0x0e78, 0x0e7b, 0x0e7e, 0x0e81, 0x0e84, 0x0e87, + 0x0e8b, 0x0e8e, 0x0e91, 0x0e94, 0x0e97, 0x0e9a, 0x0e9d, 0x0ea0, 0x0ea3, 0x0ea6, 0x0ea9, 0x0eac, + 0x0eaf, 0x0eb2, 0x0eb5, 0x0eb8, 0x0ebc, 0x0ebf, 0x0ec2, 0x0ec5, 0x0ec8, 0x0ecb, 0x0ece, 0x0ed1, + 0x0ed5, 0x0ed8, 0x0edb, 0x0ede, 0x0ee1, 0x0ee5, 0x0ee8, 0x0eeb, 0x0eee, 0x0ef1, 0x0ef5, 0x0ef8, + 0x0efb, 0x0efe, 0x0f01, 0x0f05, 0x0f08, 0x0f0b, 0x0f0e, 0x0f12, 0x0f15, 0x0f18, 0x0f1b, 0x0f1f, + 0x0f22, 0x0f25, 0x0f29, 0x0f2c, 0x0f2f, 0x0f33, 0x0f36, 0x0f39, 0x0f3d, 0x0f40, 0x0f43, 0x0f47, + 0x0f4a, 0x0f4d, 0x0f51, 0x0f54, 0x0f58, 0x0f5b, 0x0f5e, 0x0f62, 0x0f65, 0x0f69, 0x0f6c, 0x0f6f, + 0x0f73, 0x0f76, 0x0f7a, 0x0f7d, 0x0f81, 0x0f84, 0x0f88, 0x0f8b, 0x0f8f, 0x0f92, 0x0f96, 0x0f99, + 0x0f9d, 0x0fa0, 0x0fa4, 0x0fa7, 0x0fab, 0x0fae, 0x0fb2, 0x0fb5, 0x0fb9, 0x0fbd, 0x0fc0, 0x0fc4, + 0x0fc7, 0x0fcb, 0x0fcf, 0x0fd2, 0x0fd6, 0x0fd9, 0x0fdd, 0x0fe1, 0x0fe4, 0x0fe8, 0x0fec, 0x0fef, + 0x0ff3, 0x0ff7, 0x0ffa, 0x0ffe, 0x1401, 0x1403, 0x1405, 0x1406, 0x1408, 0x140a, 0x140c, 0x140e, + 0x1410, 0x1412, 0x1414, 0x1415, 0x1417, 0x1419, 0x141b, 0x141d, 0x141f, 0x1421, 0x1423, 0x1425, + 0x1427, 0x1428, 0x142a, 0x142c, 0x142e, 0x1430, 0x1432, 0x1434, 0x1436, 0x1438, 0x143a, 0x143c, + 0x143e, 0x1440, 0x1442, 0x1444, 0x1446, 0x1448, 0x144a, 0x144c, 0x144e, 0x1450, 0x1452, 0x1454, + 0x1456, 0x1458, 0x145a, 0x145c, 0x145e, 0x1460, 0x1462, 0x1464, 0x1466, 0x1468, 0x146a, 0x146c, + 0x146e, 0x1470, 0x1472, 0x1474, 0x1476, 0x1478, 0x147a, 0x147c, 0x147e, 0x1480, 0x1483, 0x1485, + 0x1487, 0x1489, 0x148b, 0x148d, 0x148f, 0x1491, 0x1493, 0x1495, 0x1498, 0x149a, 0x149c, 0x149e, + 0x14a0, 0x14a2, 0x14a4, 0x14a6, 0x14a9, 0x14ab, 0x14ad, 0x14af, 0x14b1, 0x14b3, 0x14b6, 0x14b8, + 0x14ba, 0x14bc, 0x14be, 0x14c1, 0x14c3, 0x14c5, 0x14c7, 0x14c9, 0x14cc, 0x14ce, 0x14d0, 0x14d2, + 0x14d4, 0x14d7, 0x14d9, 0x14db, 0x14dd, 0x14e0, 0x14e2, 0x14e4, 0x14e6, 0x14e9, 0x14eb, 0x14ed, + 0x14f0, 0x14f2, 0x14f4, 0x14f6, 0x14f9, 0x14fb, 0x14fd, 0x1500, 0x1502, 0x1504, 0x1507, 0x1509, + 0x150b, 0x150e, 0x1510, 0x1512, 0x1515, 0x1517, 0x1519, 0x151c, 0x151e, 0x1520, 0x1523, 0x1525, + 0x1528, 0x152a, 0x152c, 0x152f, 0x1531, 0x1533, 0x1536, 0x1538, 0x153b, 0x153d, 0x1540, 0x1542, + 0x1544, 0x1547, 0x1549, 0x154c, 0x154e, 0x1551, 0x1553, 0x1556, 0x1558, 0x155a, 0x155d, 0x155f, + 0x1562, 0x1564, 0x1567, 0x1569, 0x156c, 0x156e, 0x1571, 0x1573, 0x1576, 0x1578, 0x157b, 0x157e, + 0x1580, 0x1583, 0x1585, 0x1588, 0x158a, 0x158d, 0x158f, 0x1592, 0x1595, 0x1597, 0x159a, 0x159c, + 0x159f, 0x15a2, 0x15a4, 0x15a7, 0x15a9, 0x15ac, 0x15af, 0x15b1, 0x15b4, 0x15b6, 0x15b9, 0x15bc, + 0x15be, 0x15c1, 0x15c4, 0x15c6, 0x15c9, 0x15cc, 0x15ce, 0x15d1, 0x15d4, 0x15d7, 0x15d9, 0x15dc, + 0x15df, 0x15e1, 0x15e4, 0x15e7, 0x15ea, 0x15ec, 0x15ef, 0x15f2, 0x15f4, 0x15f7, 0x15fa, 0x15fd, + 0x1600, 0x1602, 0x1605, 0x1608, 0x160b, 0x160d, 0x1610, 0x1613, 0x1616, 0x1619, 0x161c, 0x161e, + 0x1621, 0x1624, 0x1627, 0x162a, 0x162d, 0x162f, 0x1632, 0x1635, 0x1638, 0x163b, 0x163e, 0x1641, + 0x1644, 0x1646, 0x1649, 0x164c, 0x164f, 0x1652, 0x1655, 0x1658, 0x165b, 0x165e, 0x1661, 0x1664, + 0x1667, 0x166a, 0x166d, 0x1670, 0x1673, 0x1675, 0x1678, 0x167b, 0x167e, 0x1681, 0x1684, 0x1687, + 0x168b, 0x168e, 0x1691, 0x1694, 0x1697, 0x169a, 0x169d, 0x16a0, 0x16a3, 0x16a6, 0x16a9, 0x16ac, + 0x16af, 0x16b2, 0x16b5, 0x16b8, 0x16bc, 0x16bf, 0x16c2, 0x16c5, 0x16c8, 0x16cb, 0x16ce, 0x16d1, + 0x16d5, 0x16d8, 0x16db, 0x16de, 0x16e1, 0x16e5, 0x16e8, 0x16eb, 0x16ee, 0x16f1, 0x16f5, 0x16f8, + 0x16fb, 0x16fe, 0x1701, 0x1705, 0x1708, 0x170b, 0x170e, 0x1712, 0x1715, 0x1718, 0x171b, 0x171f, + 0x1722, 0x1725, 0x1729, 0x172c, 0x172f, 0x1733, 0x1736, 0x1739, 0x173d, 0x1740, 0x1743, 0x1747, + 0x174a, 0x174d, 0x1751, 0x1754, 0x1758, 0x175b, 0x175e, 0x1762, 0x1765, 0x1769, 0x176c, 0x176f, + 0x1773, 0x1776, 0x177a, 0x177d, 0x1781, 0x1784, 0x1788, 0x178b, 0x178f, 0x1792, 0x1796, 0x1799, + 0x179d, 0x17a0, 0x17a4, 0x17a7, 0x17ab, 0x17ae, 0x17b2, 0x17b5, 0x17b9, 0x17bd, 0x17c0, 0x17c4, + 0x17c7, 0x17cb, 0x17cf, 0x17d2, 0x17d6, 0x17d9, 0x17dd, 0x17e1, 0x17e4, 0x17e8, 0x17ec, 0x17ef, + 0x17f3, 0x17f7, 0x17fa, 0x17fe, 0x1c01, 0x1c03, 0x1c05, 0x1c06, 0x1c08, 0x1c0a, 0x1c0c, 0x1c0e, + 0x1c10, 0x1c12, 0x1c14, 0x1c15, 0x1c17, 0x1c19, 0x1c1b, 0x1c1d, 0x1c1f, 0x1c21, 0x1c23, 0x1c25, + 0x1c27, 0x1c28, 0x1c2a, 0x1c2c, 0x1c2e, 0x1c30, 0x1c32, 0x1c34, 0x1c36, 0x1c38, 0x1c3a, 0x1c3c, + 0x1c3e, 0x1c40, 0x1c42, 0x1c44, 0x1c46, 0x1c48, 0x1c4a, 0x1c4c, 0x1c4e, 0x1c50, 0x1c52, 0x1c54, + 0x1c56, 0x1c58, 0x1c5a, 0x1c5c, 0x1c5e, 0x1c60, 0x1c62, 0x1c64, 0x1c66, 0x1c68, 0x1c6a, 0x1c6c, + 0x1c6e, 0x1c70, 0x1c72, 0x1c74, 0x1c76, 0x1c78, 0x1c7a, 0x1c7c, 0x1c7e, 0x1c80, 0x1c83, 0x1c85, + 0x1c87, 0x1c89, 0x1c8b, 0x1c8d, 0x1c8f, 0x1c91, 0x1c93, 0x1c95, 0x1c98, 0x1c9a, 0x1c9c, 0x1c9e, + 0x1ca0, 0x1ca2, 0x1ca4, 0x1ca6, 0x1ca9, 0x1cab, 0x1cad, 0x1caf, 0x1cb1, 0x1cb3, 0x1cb6, 0x1cb8, + 0x1cba, 0x1cbc, 0x1cbe, 0x1cc1, 0x1cc3, 0x1cc5, 0x1cc7, 0x1cc9, 0x1ccc, 0x1cce, 0x1cd0, 0x1cd2, + 0x1cd4, 0x1cd7, 0x1cd9, 0x1cdb, 0x1cdd, 0x1ce0, 0x1ce2, 0x1ce4, 0x1ce6, 0x1ce9, 0x1ceb, 0x1ced, + 0x1cf0, 0x1cf2, 0x1cf4, 0x1cf6, 0x1cf9, 0x1cfb, 0x1cfd, 0x1d00, 0x1d02, 0x1d04, 0x1d07, 0x1d09, + 0x1d0b, 0x1d0e, 0x1d10, 0x1d12, 0x1d15, 0x1d17, 0x1d19, 0x1d1c, 0x1d1e, 0x1d20, 0x1d23, 0x1d25, + 0x1d28, 0x1d2a, 0x1d2c, 0x1d2f, 0x1d31, 0x1d33, 0x1d36, 0x1d38, 0x1d3b, 0x1d3d, 0x1d40, 0x1d42, + 0x1d44, 0x1d47, 0x1d49, 0x1d4c, 0x1d4e, 0x1d51, 0x1d53, 0x1d56, 0x1d58, 0x1d5a, 0x1d5d, 0x1d5f, + 0x1d62, 0x1d64, 0x1d67, 0x1d69, 0x1d6c, 0x1d6e, 0x1d71, 0x1d73, 0x1d76, 0x1d78, 0x1d7b, 0x1d7e, + 0x1d80, 0x1d83, 0x1d85, 0x1d88, 0x1d8a, 0x1d8d, 0x1d8f, 0x1d92, 0x1d95, 0x1d97, 0x1d9a, 0x1d9c, + 0x1d9f, 0x1da2, 0x1da4, 0x1da7, 0x1da9, 0x1dac, 0x1daf, 0x1db1, 0x1db4, 0x1db6, 0x1db9, 0x1dbc, + 0x1dbe, 0x1dc1, 0x1dc4, 0x1dc6, 0x1dc9, 0x1dcc, 0x1dce, 0x1dd1, 0x1dd4, 0x1dd7, 0x1dd9, 0x1ddc, + 0x1ddf, 0x1de1, 0x1de4, 0x1de7, 0x1dea, 0x1dec, 0x1def, 0x1df2, 0x1df4, 0x1df7, 0x1dfa, 0x1dfd, + 0x1e00, 0x1e02, 0x1e05, 0x1e08, 0x1e0b, 0x1e0d, 0x1e10, 0x1e13, 0x1e16, 0x1e19, 0x1e1c, 0x1e1e, + 0x1e21, 0x1e24, 0x1e27, 0x1e2a, 0x1e2d, 0x1e2f, 0x1e32, 0x1e35, 0x1e38, 0x1e3b, 0x1e3e, 0x1e41, + 0x1e44, 0x1e46, 0x1e49, 0x1e4c, 0x1e4f, 0x1e52, 0x1e55, 0x1e58, 0x1e5b, 0x1e5e, 0x1e61, 0x1e64, + 0x1e67, 0x1e6a, 0x1e6d, 0x1e70, 0x1e73, 0x1e75, 0x1e78, 0x1e7b, 0x1e7e, 0x1e81, 0x1e84, 0x1e87, + 0x1e8b, 0x1e8e, 0x1e91, 0x1e94, 0x1e97, 0x1e9a, 0x1e9d, 0x1ea0, 0x1ea3, 0x1ea6, 0x1ea9, 0x1eac, + 0x1eaf, 0x1eb2, 0x1eb5, 0x1eb8, 0x1ebc, 0x1ebf, 0x1ec2, 0x1ec5, 0x1ec8, 0x1ecb, 0x1ece, 0x1ed1, + 0x1ed5, 0x1ed8, 0x1edb, 0x1ede, 0x1ee1, 0x1ee5, 0x1ee8, 0x1eeb, 0x1eee, 0x1ef1, 0x1ef5, 0x1ef8, + 0x1efb, 0x1efe, 0x1f01, 0x1f05, 0x1f08, 0x1f0b, 0x1f0e, 0x1f12, 0x1f15, 0x1f18, 0x1f1b, 0x1f1f, + 0x1f22, 0x1f25, 0x1f29, 0x1f2c, 0x1f2f, 0x1f33, 0x1f36, 0x1f39, 0x1f3d, 0x1f40, 0x1f43, 0x1f47, + 0x1f4a, 0x1f4d, 0x1f51, 0x1f54, 0x1f58, 0x1f5b, 0x1f5e, 0x1f62, 0x1f65, 0x1f69, 0x1f6c, 0x1f6f, + 0x1f73, 0x1f76, 0x1f7a, 0x1f7d, 0x1f81, 0x1f84, 0x1f88, 0x1f8b, 0x1f8f, 0x1f92, 0x1f96, 0x1f99, + 0x1f9d, 0x1fa0, 0x1fa4, 0x1fa7, 0x1fab, 0x1fae, 0x1fb2, 0x1fb5, 0x1fb9, 0x1fbd, 0x1fc0, 0x1fc4, + 0x1fc7, 0x1fcb, 0x1fcf, 0x1fd2, 0x1fd6, 0x1fd9, 0x1fdd, 0x1fe1, 0x1fe4, 0x1fe8, 0x1fec, 0x1fef, + 0x1ff3, 0x1ff7, 0x1ffa, 0x1ffe, 0x2401, 0x2403, 0x2405, 0x2406, 0x2408, 0x240a, 0x240c, 0x240e, + 0x2410, 0x2412, 0x2414, 0x2415, 0x2417, 0x2419, 0x241b, 0x241d, 0x241f, 0x2421, 0x2423, 0x2425, + 0x2427, 0x2428, 0x242a, 0x242c, 0x242e, 0x2430, 0x2432, 0x2434, 0x2436, 0x2438, 0x243a, 0x243c, + 0x243e, 0x2440, 0x2442, 0x2444, 0x2446, 0x2448, 0x244a, 0x244c, 0x244e, 0x2450, 0x2452, 0x2454, + 0x2456, 0x2458, 0x245a, 0x245c, 0x245e, 0x2460, 0x2462, 0x2464, 0x2466, 0x2468, 0x246a, 0x246c, + 0x246e, 0x2470, 0x2472, 0x2474, 0x2476, 0x2478, 0x247a, 0x247c, 0x247e, 0x2480, 0x2483, 0x2485, + 0x2487, 0x2489, 0x248b, 0x248d, 0x248f, 0x2491, 0x2493, 0x2495, 0x2498, 0x249a, 0x249c, 0x249e, + 0x24a0, 0x24a2, 0x24a4, 0x24a6, 0x24a9, 0x24ab, 0x24ad, 0x24af, 0x24b1, 0x24b3, 0x24b6, 0x24b8, + 0x24ba, 0x24bc, 0x24be, 0x24c1, 0x24c3, 0x24c5, 0x24c7, 0x24c9, 0x24cc, 0x24ce, 0x24d0, 0x24d2, + 0x24d4, 0x24d7, 0x24d9, 0x24db, 0x24dd, 0x24e0, 0x24e2, 0x24e4, 0x24e6, 0x24e9, 0x24eb, 0x24ed, + 0x24f0, 0x24f2, 0x24f4, 0x24f6, 0x24f9, 0x24fb, 0x24fd, 0x2500, 0x2502, 0x2504, 0x2507, 0x2509, + 0x250b, 0x250e, 0x2510, 0x2512, 0x2515, 0x2517, 0x2519, 0x251c, 0x251e, 0x2520, 0x2523, 0x2525, + 0x2528, 0x252a, 0x252c, 0x252f, 0x2531, 0x2533, 0x2536, 0x2538, 0x253b, 0x253d, 0x2540, 0x2542, + 0x2544, 0x2547, 0x2549, 0x254c, 0x254e, 0x2551, 0x2553, 0x2556, 0x2558, 0x255a, 0x255d, 0x255f, + 0x2562, 0x2564, 0x2567, 0x2569, 0x256c, 0x256e, 0x2571, 0x2573, 0x2576, 0x2578, 0x257b, 0x257e, + 0x2580, 0x2583, 0x2585, 0x2588, 0x258a, 0x258d, 0x258f, 0x2592, 0x2595, 0x2597, 0x259a, 0x259c, + 0x259f, 0x25a2, 0x25a4, 0x25a7, 0x25a9, 0x25ac, 0x25af, 0x25b1, 0x25b4, 0x25b6, 0x25b9, 0x25bc, + 0x25be, 0x25c1, 0x25c4, 0x25c6, 0x25c9, 0x25cc, 0x25ce, 0x25d1, 0x25d4, 0x25d7, 0x25d9, 0x25dc, + 0x25df, 0x25e1, 0x25e4, 0x25e7, 0x25ea, 0x25ec, 0x25ef, 0x25f2, 0x25f4, 0x25f7, 0x25fa, 0x25fd, + 0x2600, 0x2602, 0x2605, 0x2608, 0x260b, 0x260d, 0x2610, 0x2613, 0x2616, 0x2619, 0x261c, 0x261e, + 0x2621, 0x2624, 0x2627, 0x262a, 0x262d, 0x262f, 0x2632, 0x2635, 0x2638, 0x263b, 0x263e, 0x2641, + 0x2644, 0x2646, 0x2649, 0x264c, 0x264f, 0x2652, 0x2655, 0x2658, 0x265b, 0x265e, 0x2661, 0x2664, + 0x2667, 0x266a, 0x266d, 0x2670, 0x2673, 0x2675, 0x2678, 0x267b, 0x267e, 0x2681, 0x2684, 0x2687, + 0x268b, 0x268e, 0x2691, 0x2694, 0x2697, 0x269a, 0x269d, 0x26a0, 0x26a3, 0x26a6, 0x26a9, 0x26ac, + 0x26af, 0x26b2, 0x26b5, 0x26b8, 0x26bc, 0x26bf, 0x26c2, 0x26c5, 0x26c8, 0x26cb, 0x26ce, 0x26d1, + 0x26d5, 0x26d8, 0x26db, 0x26de, 0x26e1, 0x26e5, 0x26e8, 0x26eb, 0x26ee, 0x26f1, 0x26f5, 0x26f8, + 0x26fb, 0x26fe, 0x2701, 0x2705, 0x2708, 0x270b, 0x270e, 0x2712, 0x2715, 0x2718, 0x271b, 0x271f, + 0x2722, 0x2725, 0x2729, 0x272c, 0x272f, 0x2733, 0x2736, 0x2739, 0x273d, 0x2740, 0x2743, 0x2747, + 0x274a, 0x274d, 0x2751, 0x2754, 0x2758, 0x275b, 0x275e, 0x2762, 0x2765, 0x2769, 0x276c, 0x276f, + 0x2773, 0x2776, 0x277a, 0x277d, 0x2781, 0x2784, 0x2788, 0x278b, 0x278f, 0x2792, 0x2796, 0x2799, + 0x279d, 0x27a0, 0x27a4, 0x27a7, 0x27ab, 0x27ae, 0x27b2, 0x27b5, 0x27b9, 0x27bd, 0x27c0, 0x27c4, + 0x27c7, 0x27cb, 0x27cf, 0x27d2, 0x27d6, 0x27d9, 0x27dd, 0x27e1, 0x27e4, 0x27e8, 0x27ec, 0x27ef, + 0x27f3, 0x27f7, 0x27fa, 0x27fe, 0x2c01, 0x2c03, 0x2c05, 0x2c06, 0x2c08, 0x2c0a, 0x2c0c, 0x2c0e, + 0x2c10, 0x2c12, 0x2c14, 0x2c15, 0x2c17, 0x2c19, 0x2c1b, 0x2c1d, 0x2c1f, 0x2c21, 0x2c23, 0x2c25, + 0x2c27, 0x2c28, 0x2c2a, 0x2c2c, 0x2c2e, 0x2c30, 0x2c32, 0x2c34, 0x2c36, 0x2c38, 0x2c3a, 0x2c3c, + 0x2c3e, 0x2c40, 0x2c42, 0x2c44, 0x2c46, 0x2c48, 0x2c4a, 0x2c4c, 0x2c4e, 0x2c50, 0x2c52, 0x2c54, + 0x2c56, 0x2c58, 0x2c5a, 0x2c5c, 0x2c5e, 0x2c60, 0x2c62, 0x2c64, 0x2c66, 0x2c68, 0x2c6a, 0x2c6c, + 0x2c6e, 0x2c70, 0x2c72, 0x2c74, 0x2c76, 0x2c78, 0x2c7a, 0x2c7c, 0x2c7e, 0x2c80, 0x2c83, 0x2c85, + 0x2c87, 0x2c89, 0x2c8b, 0x2c8d, 0x2c8f, 0x2c91, 0x2c93, 0x2c95, 0x2c98, 0x2c9a, 0x2c9c, 0x2c9e, + 0x2ca0, 0x2ca2, 0x2ca4, 0x2ca6, 0x2ca9, 0x2cab, 0x2cad, 0x2caf, 0x2cb1, 0x2cb3, 0x2cb6, 0x2cb8, + 0x2cba, 0x2cbc, 0x2cbe, 0x2cc1, 0x2cc3, 0x2cc5, 0x2cc7, 0x2cc9, 0x2ccc, 0x2cce, 0x2cd0, 0x2cd2, + 0x2cd4, 0x2cd7, 0x2cd9, 0x2cdb, 0x2cdd, 0x2ce0, 0x2ce2, 0x2ce4, 0x2ce6, 0x2ce9, 0x2ceb, 0x2ced, + 0x2cf0, 0x2cf2, 0x2cf4, 0x2cf6, 0x2cf9, 0x2cfb, 0x2cfd, 0x2d00, 0x2d02, 0x2d04, 0x2d07, 0x2d09, + 0x2d0b, 0x2d0e, 0x2d10, 0x2d12, 0x2d15, 0x2d17, 0x2d19, 0x2d1c, 0x2d1e, 0x2d20, 0x2d23, 0x2d25, + 0x2d28, 0x2d2a, 0x2d2c, 0x2d2f, 0x2d31, 0x2d33, 0x2d36, 0x2d38, 0x2d3b, 0x2d3d, 0x2d40, 0x2d42, + 0x2d44, 0x2d47, 0x2d49, 0x2d4c, 0x2d4e, 0x2d51, 0x2d53, 0x2d56, 0x2d58, 0x2d5a, 0x2d5d, 0x2d5f, + 0x2d62, 0x2d64, 0x2d67, 0x2d69, 0x2d6c, 0x2d6e, 0x2d71, 0x2d73, 0x2d76, 0x2d78, 0x2d7b, 0x2d7e, + 0x2d80, 0x2d83, 0x2d85, 0x2d88, 0x2d8a, 0x2d8d, 0x2d8f, 0x2d92, 0x2d95, 0x2d97, 0x2d9a, 0x2d9c, + 0x2d9f, 0x2da2, 0x2da4, 0x2da7, 0x2da9, 0x2dac, 0x2daf, 0x2db1, 0x2db4, 0x2db6, 0x2db9, 0x2dbc, + 0x2dbe, 0x2dc1, 0x2dc4, 0x2dc6, 0x2dc9, 0x2dcc, 0x2dce, 0x2dd1, 0x2dd4, 0x2dd7, 0x2dd9, 0x2ddc, + 0x2ddf, 0x2de1, 0x2de4, 0x2de7, 0x2dea, 0x2dec, 0x2def, 0x2df2, 0x2df4, 0x2df7, 0x2dfa, 0x2dfd, + 0x2e00, 0x2e02, 0x2e05, 0x2e08, 0x2e0b, 0x2e0d, 0x2e10, 0x2e13, 0x2e16, 0x2e19, 0x2e1c, 0x2e1e, + 0x2e21, 0x2e24, 0x2e27, 0x2e2a, 0x2e2d, 0x2e2f, 0x2e32, 0x2e35, 0x2e38, 0x2e3b, 0x2e3e, 0x2e41, + 0x2e44, 0x2e46, 0x2e49, 0x2e4c, 0x2e4f, 0x2e52, 0x2e55, 0x2e58, 0x2e5b, 0x2e5e, 0x2e61, 0x2e64, + 0x2e67, 0x2e6a, 0x2e6d, 0x2e70, 0x2e73, 0x2e75, 0x2e78, 0x2e7b, 0x2e7e, 0x2e81, 0x2e84, 0x2e87, + 0x2e8b, 0x2e8e, 0x2e91, 0x2e94, 0x2e97, 0x2e9a, 0x2e9d, 0x2ea0, 0x2ea3, 0x2ea6, 0x2ea9, 0x2eac, + 0x2eaf, 0x2eb2, 0x2eb5, 0x2eb8, 0x2ebc, 0x2ebf, 0x2ec2, 0x2ec5, 0x2ec8, 0x2ecb, 0x2ece, 0x2ed1, + 0x2ed5, 0x2ed8, 0x2edb, 0x2ede, 0x2ee1, 0x2ee5, 0x2ee8, 0x2eeb, 0x2eee, 0x2ef1, 0x2ef5, 0x2ef8, + 0x2efb, 0x2efe, 0x2f01, 0x2f05, 0x2f08, 0x2f0b, 0x2f0e, 0x2f12, 0x2f15, 0x2f18, 0x2f1b, 0x2f1f, + 0x2f22, 0x2f25, 0x2f29, 0x2f2c, 0x2f2f, 0x2f33, 0x2f36, 0x2f39, 0x2f3d, 0x2f40, 0x2f43, 0x2f47, + 0x2f4a, 0x2f4d, 0x2f51, 0x2f54, 0x2f58, 0x2f5b, 0x2f5e, 0x2f62, 0x2f65, 0x2f69, 0x2f6c, 0x2f6f, + 0x2f73, 0x2f76, 0x2f7a, 0x2f7d, 0x2f81, 0x2f84, 0x2f88, 0x2f8b, 0x2f8f, 0x2f92, 0x2f96, 0x2f99, + 0x2f9d, 0x2fa0, 0x2fa4, 0x2fa7, 0x2fab, 0x2fae, 0x2fb2, 0x2fb5, 0x2fb9, 0x2fbd, 0x2fc0, 0x2fc4, + 0x2fc7, 0x2fcb, 0x2fcf, 0x2fd2, 0x2fd6, 0x2fd9, 0x2fdd, 0x2fe1, 0x2fe4, 0x2fe8, 0x2fec, 0x2fef, + 0x2ff3, 0x2ff7, 0x2ffa, 0x2ffe, 0x3401, 0x3403, 0x3405, 0x3406, 0x3408, 0x340a, 0x340c, 0x340e, + 0x3410, 0x3412, 0x3414, 0x3415, 0x3417, 0x3419, 0x341b, 0x341d, 0x341f, 0x3421, 0x3423, 0x3425, + 0x3427, 0x3428, 0x342a, 0x342c, 0x342e, 0x3430, 0x3432, 0x3434, 0x3436, 0x3438, 0x343a, 0x343c, + 0x343e, 0x3440, 0x3442, 0x3444, 0x3446, 0x3448, 0x344a, 0x344c, 0x344e, 0x3450, 0x3452, 0x3454, + 0x3456, 0x3458, 0x345a, 0x345c, 0x345e, 0x3460, 0x3462, 0x3464, 0x3466, 0x3468, 0x346a, 0x346c, + 0x346e, 0x3470, 0x3472, 0x3474, 0x3476, 0x3478, 0x347a, 0x347c, 0x347e, 0x3480, 0x3483, 0x3485, + 0x3487, 0x3489, 0x348b, 0x348d, 0x348f, 0x3491, 0x3493, 0x3495, 0x3498, 0x349a, 0x349c, 0x349e, + 0x34a0, 0x34a2, 0x34a4, 0x34a6, 0x34a9, 0x34ab, 0x34ad, 0x34af, 0x34b1, 0x34b3, 0x34b6, 0x34b8, + 0x34ba, 0x34bc, 0x34be, 0x34c1, 0x34c3, 0x34c5, 0x34c7, 0x34c9, 0x34cc, 0x34ce, 0x34d0, 0x34d2, + 0x34d4, 0x34d7, 0x34d9, 0x34db, 0x34dd, 0x34e0, 0x34e2, 0x34e4, 0x34e6, 0x34e9, 0x34eb, 0x34ed, + 0x34f0, 0x34f2, 0x34f4, 0x34f6, 0x34f9, 0x34fb, 0x34fd, 0x3500, 0x3502, 0x3504, 0x3507, 0x3509, + 0x350b, 0x350e, 0x3510, 0x3512, 0x3515, 0x3517, 0x3519, 0x351c, 0x351e, 0x3520, 0x3523, 0x3525, + 0x3528, 0x352a, 0x352c, 0x352f, 0x3531, 0x3533, 0x3536, 0x3538, 0x353b, 0x353d, 0x3540, 0x3542, + 0x3544, 0x3547, 0x3549, 0x354c, 0x354e, 0x3551, 0x3553, 0x3556, 0x3558, 0x355a, 0x355d, 0x355f, + 0x3562, 0x3564, 0x3567, 0x3569, 0x356c, 0x356e, 0x3571, 0x3573, 0x3576, 0x3578, 0x357b, 0x357e, + 0x3580, 0x3583, 0x3585, 0x3588, 0x358a, 0x358d, 0x358f, 0x3592, 0x3595, 0x3597, 0x359a, 0x359c, + 0x359f, 0x35a2, 0x35a4, 0x35a7, 0x35a9, 0x35ac, 0x35af, 0x35b1, 0x35b4, 0x35b6, 0x35b9, 0x35bc, + 0x35be, 0x35c1, 0x35c4, 0x35c6, 0x35c9, 0x35cc, 0x35ce, 0x35d1, 0x35d4, 0x35d7, 0x35d9, 0x35dc, + 0x35df, 0x35e1, 0x35e4, 0x35e7, 0x35ea, 0x35ec, 0x35ef, 0x35f2, 0x35f4, 0x35f7, 0x35fa, 0x35fd, + 0x3600, 0x3602, 0x3605, 0x3608, 0x360b, 0x360d, 0x3610, 0x3613, 0x3616, 0x3619, 0x361c, 0x361e, + 0x3621, 0x3624, 0x3627, 0x362a, 0x362d, 0x362f, 0x3632, 0x3635, 0x3638, 0x363b, 0x363e, 0x3641, + 0x3644, 0x3646, 0x3649, 0x364c, 0x364f, 0x3652, 0x3655, 0x3658, 0x365b, 0x365e, 0x3661, 0x3664, + 0x3667, 0x366a, 0x366d, 0x3670, 0x3673, 0x3675, 0x3678, 0x367b, 0x367e, 0x3681, 0x3684, 0x3687, + 0x368b, 0x368e, 0x3691, 0x3694, 0x3697, 0x369a, 0x369d, 0x36a0, 0x36a3, 0x36a6, 0x36a9, 0x36ac, + 0x36af, 0x36b2, 0x36b5, 0x36b8, 0x36bc, 0x36bf, 0x36c2, 0x36c5, 0x36c8, 0x36cb, 0x36ce, 0x36d1, + 0x36d5, 0x36d8, 0x36db, 0x36de, 0x36e1, 0x36e5, 0x36e8, 0x36eb, 0x36ee, 0x36f1, 0x36f5, 0x36f8, + 0x36fb, 0x36fe, 0x3701, 0x3705, 0x3708, 0x370b, 0x370e, 0x3712, 0x3715, 0x3718, 0x371b, 0x371f, + 0x3722, 0x3725, 0x3729, 0x372c, 0x372f, 0x3733, 0x3736, 0x3739, 0x373d, 0x3740, 0x3743, 0x3747, + 0x374a, 0x374d, 0x3751, 0x3754, 0x3758, 0x375b, 0x375e, 0x3762, 0x3765, 0x3769, 0x376c, 0x376f, + 0x3773, 0x3776, 0x377a, 0x377d, 0x3781, 0x3784, 0x3788, 0x378b, 0x378f, 0x3792, 0x3796, 0x3799, + 0x379d, 0x37a0, 0x37a4, 0x37a7, 0x37ab, 0x37ae, 0x37b2, 0x37b5, 0x37b9, 0x37bd, 0x37c0, 0x37c4, + 0x37c7, 0x37cb, 0x37cf, 0x37d2, 0x37d6, 0x37d9, 0x37dd, 0x37e1, 0x37e4, 0x37e8, 0x37ec, 0x37ef, + 0x37f3, 0x37f7, 0x37fa, 0x37fe, 0x3c01, 0x3c03, 0x3c05, 0x3c06, 0x3c08, 0x3c0a, 0x3c0c, 0x3c0e, + 0x3c10, 0x3c12, 0x3c14, 0x3c15, 0x3c17, 0x3c19, 0x3c1b, 0x3c1d, 0x3c1f, 0x3c21, 0x3c23, 0x3c25, + 0x3c27, 0x3c28, 0x3c2a, 0x3c2c, 0x3c2e, 0x3c30, 0x3c32, 0x3c34, 0x3c36, 0x3c38, 0x3c3a, 0x3c3c, + 0x3c3e, 0x3c40, 0x3c42, 0x3c44, 0x3c46, 0x3c48, 0x3c4a, 0x3c4c, 0x3c4e, 0x3c50, 0x3c52, 0x3c54, + 0x3c56, 0x3c58, 0x3c5a, 0x3c5c, 0x3c5e, 0x3c60, 0x3c62, 0x3c64, 0x3c66, 0x3c68, 0x3c6a, 0x3c6c, + 0x3c6e, 0x3c70, 0x3c72, 0x3c74, 0x3c76, 0x3c78, 0x3c7a, 0x3c7c, 0x3c7e, 0x3c80, 0x3c83, 0x3c85, + 0x3c87, 0x3c89, 0x3c8b, 0x3c8d, 0x3c8f, 0x3c91, 0x3c93, 0x3c95, 0x3c98, 0x3c9a, 0x3c9c, 0x3c9e, + 0x3ca0, 0x3ca2, 0x3ca4, 0x3ca6, 0x3ca9, 0x3cab, 0x3cad, 0x3caf, 0x3cb1, 0x3cb3, 0x3cb6, 0x3cb8, + 0x3cba, 0x3cbc, 0x3cbe, 0x3cc1, 0x3cc3, 0x3cc5, 0x3cc7, 0x3cc9, 0x3ccc, 0x3cce, 0x3cd0, 0x3cd2 +}; +static_assert(sizeof(centTableFM) == sizeof(uint16_t) * Note::ABS_PITCH_RANGE, +"Invalid FM cent table size."); + +const uint16_t centTableSSGSquare[3072] = { + 0xee8, 0xee1, 0xeda, 0xed4, 0xecd, 0xec6, 0xebf, 0xeb8, 0xeb1, 0xeab, 0xea4, 0xe9d, + 0xe96, 0xe90, 0xe89, 0xe82, 0xe7c, 0xe75, 0xe6e, 0xe67, 0xe61, 0xe5a, 0xe54, 0xe4d, + 0xe46, 0xe40, 0xe39, 0xe33, 0xe2c, 0xe26, 0xe1f, 0xe18, 0xe12, 0xe0b, 0xe05, 0xdff, + 0xdf8, 0xdf2, 0xdeb, 0xde5, 0xdde, 0xdd8, 0xdd2, 0xdcb, 0xdc5, 0xdbe, 0xdb8, 0xdb2, + 0xdab, 0xda5, 0xd9f, 0xd99, 0xd92, 0xd8c, 0xd86, 0xd7f, 0xd79, 0xd73, 0xd6d, 0xd67, + 0xd60, 0xd5a, 0xd54, 0xd4e, 0xd48, 0xd42, 0xd3c, 0xd35, 0xd2f, 0xd29, 0xd23, 0xd1d, + 0xd17, 0xd11, 0xd0b, 0xd05, 0xcff, 0xcf9, 0xcf3, 0xced, 0xce7, 0xce1, 0xcdb, 0xcd5, + 0xccf, 0xcc9, 0xcc3, 0xcbe, 0xcb8, 0xcb2, 0xcac, 0xca6, 0xca0, 0xc9a, 0xc95, 0xc8f, + 0xc89, 0xc83, 0xc7d, 0xc78, 0xc72, 0xc6c, 0xc66, 0xc61, 0xc5b, 0xc55, 0xc50, 0xc4a, + 0xc44, 0xc3f, 0xc39, 0xc33, 0xc2e, 0xc28, 0xc22, 0xc1d, 0xc17, 0xc12, 0xc0c, 0xc06, + 0xc01, 0xbfb, 0xbf6, 0xbf0, 0xbeb, 0xbe5, 0xbe0, 0xbda, 0xbd5, 0xbcf, 0xbca, 0xbc5, + 0xbbf, 0xbba, 0xbb4, 0xbaf, 0xba9, 0xba4, 0xb9f, 0xb99, 0xb94, 0xb8f, 0xb89, 0xb84, + 0xb7f, 0xb79, 0xb74, 0xb6f, 0xb69, 0xb64, 0xb5f, 0xb5a, 0xb54, 0xb4f, 0xb4a, 0xb45, + 0xb40, 0xb3a, 0xb35, 0xb30, 0xb2b, 0xb26, 0xb21, 0xb1b, 0xb16, 0xb11, 0xb0c, 0xb07, + 0xb02, 0xafd, 0xaf8, 0xaf3, 0xaee, 0xae9, 0xae4, 0xadf, 0xad9, 0xad4, 0xacf, 0xaca, + 0xac6, 0xac1, 0xabc, 0xab7, 0xab2, 0xaad, 0xaa8, 0xaa3, 0xa9e, 0xa99, 0xa94, 0xa8f, + 0xa8a, 0xa86, 0xa81, 0xa7c, 0xa77, 0xa72, 0xa6d, 0xa69, 0xa64, 0xa5f, 0xa5a, 0xa55, + 0xa51, 0xa4c, 0xa47, 0xa42, 0xa3e, 0xa39, 0xa34, 0xa2f, 0xa2b, 0xa26, 0xa21, 0xa1d, + 0xa18, 0xa13, 0xa0f, 0xa0a, 0xa05, 0xa01, 0x9fc, 0x9f8, 0x9f3, 0x9ee, 0x9ea, 0x9e5, + 0x9e1, 0x9dc, 0x9d8, 0x9d3, 0x9ce, 0x9ca, 0x9c5, 0x9c1, 0x9bc, 0x9b8, 0x9b3, 0x9af, + 0x9aa, 0x9a6, 0x9a2, 0x99d, 0x999, 0x994, 0x990, 0x98b, 0x987, 0x983, 0x97e, 0x97a, + 0x975, 0x971, 0x96d, 0x968, 0x964, 0x960, 0x95b, 0x957, 0x953, 0x94e, 0x94a, 0x946, + 0x942, 0x93d, 0x939, 0x935, 0x931, 0x92c, 0x928, 0x924, 0x920, 0x91b, 0x917, 0x913, + 0x90f, 0x90b, 0x906, 0x902, 0x8fe, 0x8fa, 0x8f6, 0x8f2, 0x8ee, 0x8e9, 0x8e5, 0x8e1, + 0x8dd, 0x8d9, 0x8d5, 0x8d1, 0x8cd, 0x8c9, 0x8c5, 0x8c1, 0x8bd, 0x8b9, 0x8b4, 0x8b0, + 0x8ac, 0x8a8, 0x8a4, 0x8a0, 0x89c, 0x899, 0x895, 0x891, 0x88d, 0x889, 0x885, 0x881, + 0x87d, 0x879, 0x875, 0x871, 0x86d, 0x869, 0x865, 0x862, 0x85e, 0x85a, 0x856, 0x852, + 0x84e, 0x84a, 0x847, 0x843, 0x83f, 0x83b, 0x837, 0x834, 0x830, 0x82c, 0x828, 0x825, + 0x821, 0x81d, 0x819, 0x816, 0x812, 0x80e, 0x80a, 0x807, 0x803, 0x7ff, 0x7fc, 0x7f8, + 0x7f4, 0x7f1, 0x7ed, 0x7e9, 0x7e6, 0x7e2, 0x7de, 0x7db, 0x7d7, 0x7d3, 0x7d0, 0x7cc, + 0x7c9, 0x7c5, 0x7c1, 0x7be, 0x7ba, 0x7b7, 0x7b3, 0x7b0, 0x7ac, 0x7a8, 0x7a5, 0x7a1, + 0x79e, 0x79a, 0x797, 0x793, 0x790, 0x78c, 0x789, 0x785, 0x782, 0x77e, 0x77b, 0x778, + 0x774, 0x771, 0x76d, 0x76a, 0x766, 0x763, 0x760, 0x75c, 0x759, 0x755, 0x752, 0x74f, + 0x74b, 0x748, 0x744, 0x741, 0x73e, 0x73a, 0x737, 0x734, 0x730, 0x72d, 0x72a, 0x726, + 0x723, 0x720, 0x71d, 0x719, 0x716, 0x713, 0x70f, 0x70c, 0x709, 0x706, 0x702, 0x6ff, + 0x6fc, 0x6f9, 0x6f6, 0x6f2, 0x6ef, 0x6ec, 0x6e9, 0x6e6, 0x6e2, 0x6df, 0x6dc, 0x6d9, + 0x6d6, 0x6d3, 0x6cf, 0x6cc, 0x6c9, 0x6c6, 0x6c3, 0x6c0, 0x6bd, 0x6ba, 0x6b6, 0x6b3, + 0x6b0, 0x6ad, 0x6aa, 0x6a7, 0x6a4, 0x6a1, 0x69e, 0x69b, 0x698, 0x695, 0x692, 0x68f, + 0x68c, 0x689, 0x685, 0x682, 0x67f, 0x67c, 0x679, 0x676, 0x674, 0x671, 0x66e, 0x66b, + 0x668, 0x665, 0x662, 0x65f, 0x65c, 0x659, 0x656, 0x653, 0x650, 0x64d, 0x64a, 0x647, + 0x644, 0x642, 0x63f, 0x63c, 0x639, 0x636, 0x633, 0x630, 0x62d, 0x62b, 0x628, 0x625, + 0x622, 0x61f, 0x61c, 0x61a, 0x617, 0x614, 0x611, 0x60e, 0x60c, 0x609, 0x606, 0x603, + 0x600, 0x5fe, 0x5fb, 0x5f8, 0x5f5, 0x5f3, 0x5f0, 0x5ed, 0x5ea, 0x5e8, 0x5e5, 0x5e2, + 0x5e0, 0x5dd, 0x5da, 0x5d7, 0x5d5, 0x5d2, 0x5cf, 0x5cd, 0x5ca, 0x5c7, 0x5c5, 0x5c2, + 0x5bf, 0x5bd, 0x5ba, 0x5b7, 0x5b5, 0x5b2, 0x5af, 0x5ad, 0x5aa, 0x5a8, 0x5a5, 0x5a2, + 0x5a0, 0x59d, 0x59b, 0x598, 0x595, 0x593, 0x590, 0x58e, 0x58b, 0x589, 0x586, 0x583, + 0x581, 0x57e, 0x57c, 0x579, 0x577, 0x574, 0x572, 0x56f, 0x56d, 0x56a, 0x568, 0x565, + 0x563, 0x560, 0x55e, 0x55b, 0x559, 0x556, 0x554, 0x551, 0x54f, 0x54d, 0x54a, 0x548, + 0x545, 0x543, 0x540, 0x53e, 0x53c, 0x539, 0x537, 0x534, 0x532, 0x52f, 0x52d, 0x52b, + 0x528, 0x526, 0x524, 0x521, 0x51f, 0x51c, 0x51a, 0x518, 0x515, 0x513, 0x511, 0x50e, + 0x50c, 0x50a, 0x507, 0x505, 0x503, 0x500, 0x4fe, 0x4fc, 0x4f9, 0x4f7, 0x4f5, 0x4f3, + 0x4f0, 0x4ee, 0x4ec, 0x4e9, 0x4e7, 0x4e5, 0x4e3, 0x4e0, 0x4de, 0x4dc, 0x4da, 0x4d7, + 0x4d5, 0x4d3, 0x4d1, 0x4cf, 0x4cc, 0x4ca, 0x4c8, 0x4c6, 0x4c3, 0x4c1, 0x4bf, 0x4bd, + 0x4bb, 0x4b9, 0x4b6, 0x4b4, 0x4b2, 0x4b0, 0x4ae, 0x4ac, 0x4a9, 0x4a7, 0x4a5, 0x4a3, + 0x4a1, 0x49f, 0x49d, 0x49a, 0x498, 0x496, 0x494, 0x492, 0x490, 0x48e, 0x48c, 0x489, + 0x487, 0x485, 0x483, 0x481, 0x47f, 0x47d, 0x47b, 0x479, 0x477, 0x475, 0x473, 0x471, + 0x46f, 0x46c, 0x46a, 0x468, 0x466, 0x464, 0x462, 0x460, 0x45e, 0x45c, 0x45a, 0x458, + 0x456, 0x454, 0x452, 0x450, 0x44e, 0x44c, 0x44a, 0x448, 0x446, 0x444, 0x442, 0x440, + 0x43e, 0x43c, 0x43b, 0x439, 0x437, 0x435, 0x433, 0x431, 0x42f, 0x42d, 0x42b, 0x429, + 0x427, 0x425, 0x423, 0x421, 0x420, 0x41e, 0x41c, 0x41a, 0x418, 0x416, 0x414, 0x412, + 0x410, 0x40f, 0x40d, 0x40b, 0x409, 0x407, 0x405, 0x403, 0x401, 0x400, 0x3fe, 0x3fc, + 0x3fa, 0x3f8, 0x3f6, 0x3f5, 0x3f3, 0x3f1, 0x3ef, 0x3ed, 0x3eb, 0x3ea, 0x3e8, 0x3e6, + 0x3e4, 0x3e2, 0x3e1, 0x3df, 0x3dd, 0x3db, 0x3da, 0x3d8, 0x3d6, 0x3d4, 0x3d2, 0x3d1, + 0x3cf, 0x3cd, 0x3cb, 0x3ca, 0x3c8, 0x3c6, 0x3c4, 0x3c3, 0x3c1, 0x3bf, 0x3bd, 0x3bc, + 0x3ba, 0x3b8, 0x3b7, 0x3b5, 0x3b3, 0x3b1, 0x3b0, 0x3ae, 0x3ac, 0x3ab, 0x3a9, 0x3a7, + 0x3a6, 0x3a4, 0x3a2, 0x3a1, 0x39f, 0x39d, 0x39c, 0x39a, 0x398, 0x397, 0x395, 0x393, + 0x392, 0x390, 0x38e, 0x38d, 0x38b, 0x389, 0x388, 0x386, 0x384, 0x383, 0x381, 0x380, + 0x37e, 0x37c, 0x37b, 0x379, 0x378, 0x376, 0x374, 0x373, 0x371, 0x370, 0x36e, 0x36c, + 0x36b, 0x369, 0x368, 0x366, 0x365, 0x363, 0x361, 0x360, 0x35e, 0x35d, 0x35b, 0x35a, + 0x358, 0x357, 0x355, 0x353, 0x352, 0x350, 0x34f, 0x34d, 0x34c, 0x34a, 0x349, 0x347, + 0x346, 0x344, 0x343, 0x341, 0x340, 0x33e, 0x33d, 0x33b, 0x33a, 0x338, 0x337, 0x335, + 0x334, 0x332, 0x331, 0x32f, 0x32e, 0x32c, 0x32b, 0x32a, 0x328, 0x327, 0x325, 0x324, + 0x322, 0x321, 0x31f, 0x31e, 0x31c, 0x31b, 0x31a, 0x318, 0x317, 0x315, 0x314, 0x312, + 0x311, 0x310, 0x30e, 0x30d, 0x30b, 0x30a, 0x309, 0x307, 0x306, 0x304, 0x303, 0x302, + 0x300, 0x2ff, 0x2fd, 0x2fc, 0x2fb, 0x2f9, 0x2f8, 0x2f7, 0x2f5, 0x2f4, 0x2f2, 0x2f1, + 0x2f0, 0x2ee, 0x2ed, 0x2ec, 0x2ea, 0x2e9, 0x2e8, 0x2e6, 0x2e5, 0x2e4, 0x2e2, 0x2e1, + 0x2e0, 0x2de, 0x2dd, 0x2dc, 0x2da, 0x2d9, 0x2d8, 0x2d6, 0x2d5, 0x2d4, 0x2d3, 0x2d1, + 0x2d0, 0x2cf, 0x2cd, 0x2cc, 0x2cb, 0x2c9, 0x2c8, 0x2c7, 0x2c6, 0x2c4, 0x2c3, 0x2c2, + 0x2c0, 0x2bf, 0x2be, 0x2bd, 0x2bb, 0x2ba, 0x2b9, 0x2b8, 0x2b6, 0x2b5, 0x2b4, 0x2b3, + 0x2b1, 0x2b0, 0x2af, 0x2ae, 0x2ac, 0x2ab, 0x2aa, 0x2a9, 0x2a7, 0x2a6, 0x2a5, 0x2a4, + 0x2a3, 0x2a1, 0x2a0, 0x29f, 0x29e, 0x29d, 0x29b, 0x29a, 0x299, 0x298, 0x297, 0x295, + 0x294, 0x293, 0x292, 0x291, 0x28f, 0x28e, 0x28d, 0x28c, 0x28b, 0x28a, 0x288, 0x287, + 0x286, 0x285, 0x284, 0x283, 0x281, 0x280, 0x27f, 0x27e, 0x27d, 0x27c, 0x27a, 0x279, + 0x278, 0x277, 0x276, 0x275, 0x274, 0x272, 0x271, 0x270, 0x26f, 0x26e, 0x26d, 0x26c, + 0x26b, 0x26a, 0x268, 0x267, 0x266, 0x265, 0x264, 0x263, 0x262, 0x261, 0x260, 0x25e, + 0x25d, 0x25c, 0x25b, 0x25a, 0x259, 0x258, 0x257, 0x256, 0x255, 0x254, 0x253, 0x251, + 0x250, 0x24f, 0x24e, 0x24d, 0x24c, 0x24b, 0x24a, 0x249, 0x248, 0x247, 0x246, 0x245, + 0x244, 0x243, 0x242, 0x241, 0x240, 0x23e, 0x23d, 0x23c, 0x23b, 0x23a, 0x239, 0x238, + 0x237, 0x236, 0x235, 0x234, 0x233, 0x232, 0x231, 0x230, 0x22f, 0x22e, 0x22d, 0x22c, + 0x22b, 0x22a, 0x229, 0x228, 0x227, 0x226, 0x225, 0x224, 0x223, 0x222, 0x221, 0x220, + 0x21f, 0x21e, 0x21d, 0x21c, 0x21b, 0x21a, 0x219, 0x218, 0x217, 0x216, 0x216, 0x215, + 0x214, 0x213, 0x212, 0x211, 0x210, 0x20f, 0x20e, 0x20d, 0x20c, 0x20b, 0x20a, 0x209, + 0x208, 0x207, 0x206, 0x205, 0x204, 0x204, 0x203, 0x202, 0x201, 0x200, 0x1ff, 0x1fe, + 0x1fd, 0x1fc, 0x1fb, 0x1fa, 0x1f9, 0x1f8, 0x1f8, 0x1f7, 0x1f6, 0x1f5, 0x1f4, 0x1f3, + 0x1f2, 0x1f1, 0x1f0, 0x1ef, 0x1ef, 0x1ee, 0x1ed, 0x1ec, 0x1eb, 0x1ea, 0x1e9, 0x1e8, + 0x1e7, 0x1e7, 0x1e6, 0x1e5, 0x1e4, 0x1e3, 0x1e2, 0x1e1, 0x1e0, 0x1e0, 0x1df, 0x1de, + 0x1dd, 0x1dc, 0x1db, 0x1da, 0x1da, 0x1d9, 0x1d8, 0x1d7, 0x1d6, 0x1d5, 0x1d4, 0x1d4, + 0x1d3, 0x1d2, 0x1d1, 0x1d0, 0x1cf, 0x1cf, 0x1ce, 0x1cd, 0x1cc, 0x1cb, 0x1ca, 0x1ca, + 0x1c9, 0x1c8, 0x1c7, 0x1c6, 0x1c6, 0x1c5, 0x1c4, 0x1c3, 0x1c2, 0x1c1, 0x1c1, 0x1c0, + 0x1bf, 0x1be, 0x1bd, 0x1bd, 0x1bc, 0x1bb, 0x1ba, 0x1b9, 0x1b9, 0x1b8, 0x1b7, 0x1b6, + 0x1b5, 0x1b5, 0x1b4, 0x1b3, 0x1b2, 0x1b1, 0x1b1, 0x1b0, 0x1af, 0x1ae, 0x1ae, 0x1ad, + 0x1ac, 0x1ab, 0x1ab, 0x1aa, 0x1a9, 0x1a8, 0x1a7, 0x1a7, 0x1a6, 0x1a5, 0x1a4, 0x1a4, + 0x1a3, 0x1a2, 0x1a1, 0x1a1, 0x1a0, 0x19f, 0x19e, 0x19e, 0x19d, 0x19c, 0x19b, 0x19b, + 0x19a, 0x199, 0x198, 0x198, 0x197, 0x196, 0x195, 0x195, 0x194, 0x193, 0x193, 0x192, + 0x191, 0x190, 0x190, 0x18f, 0x18e, 0x18e, 0x18d, 0x18c, 0x18b, 0x18b, 0x18a, 0x189, + 0x189, 0x188, 0x187, 0x186, 0x186, 0x185, 0x184, 0x184, 0x183, 0x182, 0x182, 0x181, + 0x180, 0x17f, 0x17f, 0x17e, 0x17d, 0x17d, 0x17c, 0x17b, 0x17b, 0x17a, 0x179, 0x179, + 0x178, 0x177, 0x177, 0x176, 0x175, 0x175, 0x174, 0x173, 0x172, 0x172, 0x171, 0x170, + 0x170, 0x16f, 0x16f, 0x16e, 0x16d, 0x16d, 0x16c, 0x16b, 0x16b, 0x16a, 0x169, 0x169, + 0x168, 0x167, 0x167, 0x166, 0x165, 0x165, 0x164, 0x163, 0x163, 0x162, 0x162, 0x161, + 0x160, 0x160, 0x15f, 0x15e, 0x15e, 0x15d, 0x15c, 0x15c, 0x15b, 0x15b, 0x15a, 0x159, + 0x159, 0x158, 0x157, 0x157, 0x156, 0x156, 0x155, 0x154, 0x154, 0x153, 0x153, 0x152, + 0x151, 0x151, 0x150, 0x14f, 0x14f, 0x14e, 0x14e, 0x14d, 0x14c, 0x14c, 0x14b, 0x14b, + 0x14a, 0x149, 0x149, 0x148, 0x148, 0x147, 0x147, 0x146, 0x145, 0x145, 0x144, 0x144, + 0x143, 0x142, 0x142, 0x141, 0x141, 0x140, 0x140, 0x13f, 0x13e, 0x13e, 0x13d, 0x13d, + 0x13c, 0x13c, 0x13b, 0x13a, 0x13a, 0x139, 0x139, 0x138, 0x138, 0x137, 0x136, 0x136, + 0x135, 0x135, 0x134, 0x134, 0x133, 0x133, 0x132, 0x131, 0x131, 0x130, 0x130, 0x12f, + 0x12f, 0x12e, 0x12e, 0x12d, 0x12d, 0x12c, 0x12b, 0x12b, 0x12a, 0x12a, 0x129, 0x129, + 0x128, 0x128, 0x127, 0x127, 0x126, 0x126, 0x125, 0x124, 0x124, 0x123, 0x123, 0x122, + 0x122, 0x121, 0x121, 0x120, 0x120, 0x11f, 0x11f, 0x11e, 0x11e, 0x11d, 0x11d, 0x11c, + 0x11c, 0x11b, 0x11b, 0x11a, 0x11a, 0x119, 0x119, 0x118, 0x118, 0x117, 0x117, 0x116, + 0x116, 0x115, 0x115, 0x114, 0x114, 0x113, 0x113, 0x112, 0x112, 0x111, 0x111, 0x110, + 0x110, 0x10f, 0x10f, 0x10e, 0x10e, 0x10d, 0x10d, 0x10c, 0x10c, 0x10b, 0x10b, 0x10a, + 0x10a, 0x109, 0x109, 0x108, 0x108, 0x107, 0x107, 0x106, 0x106, 0x106, 0x105, 0x105, + 0x104, 0x104, 0x103, 0x103, 0x102, 0x102, 0x101, 0x101, 0x100, 0x100, 0x0ff, 0x0ff, + 0x0ff, 0x0fe, 0x0fe, 0x0fd, 0x0fd, 0x0fc, 0x0fc, 0x0fb, 0x0fb, 0x0fa, 0x0fa, 0x0fa, + 0x0f9, 0x0f9, 0x0f8, 0x0f8, 0x0f7, 0x0f7, 0x0f6, 0x0f6, 0x0f5, 0x0f5, 0x0f5, 0x0f4, + 0x0f4, 0x0f3, 0x0f3, 0x0f2, 0x0f2, 0x0f2, 0x0f1, 0x0f1, 0x0f0, 0x0f0, 0x0ef, 0x0ef, + 0x0ef, 0x0ee, 0x0ee, 0x0ed, 0x0ed, 0x0ec, 0x0ec, 0x0ec, 0x0eb, 0x0eb, 0x0ea, 0x0ea, + 0x0e9, 0x0e9, 0x0e9, 0x0e8, 0x0e8, 0x0e7, 0x0e7, 0x0e6, 0x0e6, 0x0e6, 0x0e5, 0x0e5, + 0x0e4, 0x0e4, 0x0e4, 0x0e3, 0x0e3, 0x0e2, 0x0e2, 0x0e2, 0x0e1, 0x0e1, 0x0e0, 0x0e0, + 0x0e0, 0x0df, 0x0df, 0x0de, 0x0de, 0x0dd, 0x0dd, 0x0dd, 0x0dc, 0x0dc, 0x0dc, 0x0db, + 0x0db, 0x0da, 0x0da, 0x0da, 0x0d9, 0x0d9, 0x0d8, 0x0d8, 0x0d8, 0x0d7, 0x0d7, 0x0d6, + 0x0d6, 0x0d6, 0x0d5, 0x0d5, 0x0d4, 0x0d4, 0x0d4, 0x0d3, 0x0d3, 0x0d3, 0x0d2, 0x0d2, + 0x0d1, 0x0d1, 0x0d1, 0x0d0, 0x0d0, 0x0d0, 0x0cf, 0x0cf, 0x0ce, 0x0ce, 0x0ce, 0x0cd, + 0x0cd, 0x0cd, 0x0cc, 0x0cc, 0x0cb, 0x0cb, 0x0cb, 0x0ca, 0x0ca, 0x0ca, 0x0c9, 0x0c9, + 0x0c9, 0x0c8, 0x0c8, 0x0c7, 0x0c7, 0x0c7, 0x0c6, 0x0c6, 0x0c6, 0x0c5, 0x0c5, 0x0c5, + 0x0c4, 0x0c4, 0x0c4, 0x0c3, 0x0c3, 0x0c3, 0x0c2, 0x0c2, 0x0c1, 0x0c1, 0x0c1, 0x0c0, + 0x0c0, 0x0c0, 0x0bf, 0x0bf, 0x0bf, 0x0be, 0x0be, 0x0be, 0x0bd, 0x0bd, 0x0bd, 0x0bc, + 0x0bc, 0x0bc, 0x0bb, 0x0bb, 0x0bb, 0x0ba, 0x0ba, 0x0ba, 0x0b9, 0x0b9, 0x0b9, 0x0b8, + 0x0b8, 0x0b8, 0x0b7, 0x0b7, 0x0b7, 0x0b6, 0x0b6, 0x0b6, 0x0b5, 0x0b5, 0x0b5, 0x0b4, + 0x0b4, 0x0b4, 0x0b3, 0x0b3, 0x0b3, 0x0b2, 0x0b2, 0x0b2, 0x0b1, 0x0b1, 0x0b1, 0x0b0, + 0x0b0, 0x0b0, 0x0af, 0x0af, 0x0af, 0x0af, 0x0ae, 0x0ae, 0x0ae, 0x0ad, 0x0ad, 0x0ad, + 0x0ac, 0x0ac, 0x0ac, 0x0ab, 0x0ab, 0x0ab, 0x0aa, 0x0aa, 0x0aa, 0x0aa, 0x0a9, 0x0a9, + 0x0a9, 0x0a8, 0x0a8, 0x0a8, 0x0a7, 0x0a7, 0x0a7, 0x0a7, 0x0a6, 0x0a6, 0x0a6, 0x0a5, + 0x0a5, 0x0a5, 0x0a4, 0x0a4, 0x0a4, 0x0a4, 0x0a3, 0x0a3, 0x0a3, 0x0a2, 0x0a2, 0x0a2, + 0x0a2, 0x0a1, 0x0a1, 0x0a1, 0x0a0, 0x0a0, 0x0a0, 0x09f, 0x09f, 0x09f, 0x09f, 0x09e, + 0x09e, 0x09e, 0x09d, 0x09d, 0x09d, 0x09d, 0x09c, 0x09c, 0x09c, 0x09b, 0x09b, 0x09b, + 0x09b, 0x09a, 0x09a, 0x09a, 0x09a, 0x099, 0x099, 0x099, 0x098, 0x098, 0x098, 0x098, + 0x097, 0x097, 0x097, 0x097, 0x096, 0x096, 0x096, 0x095, 0x095, 0x095, 0x095, 0x094, + 0x094, 0x094, 0x094, 0x093, 0x093, 0x093, 0x093, 0x092, 0x092, 0x092, 0x091, 0x091, + 0x091, 0x091, 0x090, 0x090, 0x090, 0x090, 0x08f, 0x08f, 0x08f, 0x08f, 0x08e, 0x08e, + 0x08e, 0x08e, 0x08d, 0x08d, 0x08d, 0x08d, 0x08c, 0x08c, 0x08c, 0x08c, 0x08b, 0x08b, + 0x08b, 0x08b, 0x08a, 0x08a, 0x08a, 0x08a, 0x089, 0x089, 0x089, 0x089, 0x088, 0x088, + 0x088, 0x088, 0x087, 0x087, 0x087, 0x087, 0x086, 0x086, 0x086, 0x086, 0x085, 0x085, + 0x085, 0x085, 0x084, 0x084, 0x084, 0x084, 0x083, 0x083, 0x083, 0x083, 0x083, 0x082, + 0x082, 0x082, 0x082, 0x081, 0x081, 0x081, 0x081, 0x080, 0x080, 0x080, 0x080, 0x07f, + 0x07f, 0x07f, 0x07f, 0x07f, 0x07e, 0x07e, 0x07e, 0x07e, 0x07d, 0x07d, 0x07d, 0x07d, + 0x07d, 0x07c, 0x07c, 0x07c, 0x07c, 0x07b, 0x07b, 0x07b, 0x07b, 0x07b, 0x07a, 0x07a, + 0x07a, 0x07a, 0x079, 0x079, 0x079, 0x079, 0x079, 0x078, 0x078, 0x078, 0x078, 0x077, + 0x077, 0x077, 0x077, 0x077, 0x076, 0x076, 0x076, 0x076, 0x076, 0x075, 0x075, 0x075, + 0x075, 0x074, 0x074, 0x074, 0x074, 0x074, 0x073, 0x073, 0x073, 0x073, 0x073, 0x072, + 0x072, 0x072, 0x072, 0x072, 0x071, 0x071, 0x071, 0x071, 0x071, 0x070, 0x070, 0x070, + 0x070, 0x070, 0x06f, 0x06f, 0x06f, 0x06f, 0x06f, 0x06e, 0x06e, 0x06e, 0x06e, 0x06e, + 0x06d, 0x06d, 0x06d, 0x06d, 0x06d, 0x06c, 0x06c, 0x06c, 0x06c, 0x06c, 0x06b, 0x06b, + 0x06b, 0x06b, 0x06b, 0x06a, 0x06a, 0x06a, 0x06a, 0x06a, 0x069, 0x069, 0x069, 0x069, + 0x069, 0x069, 0x068, 0x068, 0x068, 0x068, 0x068, 0x067, 0x067, 0x067, 0x067, 0x067, + 0x066, 0x066, 0x066, 0x066, 0x066, 0x066, 0x065, 0x065, 0x065, 0x065, 0x065, 0x064, + 0x064, 0x064, 0x064, 0x064, 0x064, 0x063, 0x063, 0x063, 0x063, 0x063, 0x062, 0x062, + 0x062, 0x062, 0x062, 0x062, 0x061, 0x061, 0x061, 0x061, 0x061, 0x061, 0x060, 0x060, + 0x060, 0x060, 0x060, 0x060, 0x05f, 0x05f, 0x05f, 0x05f, 0x05f, 0x05e, 0x05e, 0x05e, + 0x05e, 0x05e, 0x05e, 0x05d, 0x05d, 0x05d, 0x05d, 0x05d, 0x05d, 0x05c, 0x05c, 0x05c, + 0x05c, 0x05c, 0x05c, 0x05b, 0x05b, 0x05b, 0x05b, 0x05b, 0x05b, 0x05a, 0x05a, 0x05a, + 0x05a, 0x05a, 0x05a, 0x05a, 0x059, 0x059, 0x059, 0x059, 0x059, 0x059, 0x058, 0x058, + 0x058, 0x058, 0x058, 0x058, 0x057, 0x057, 0x057, 0x057, 0x057, 0x057, 0x056, 0x056, + 0x056, 0x056, 0x056, 0x056, 0x056, 0x055, 0x055, 0x055, 0x055, 0x055, 0x055, 0x054, + 0x054, 0x054, 0x054, 0x054, 0x054, 0x054, 0x053, 0x053, 0x053, 0x053, 0x053, 0x053, + 0x053, 0x052, 0x052, 0x052, 0x052, 0x052, 0x052, 0x051, 0x051, 0x051, 0x051, 0x051, + 0x051, 0x051, 0x050, 0x050, 0x050, 0x050, 0x050, 0x050, 0x050, 0x04f, 0x04f, 0x04f, + 0x04f, 0x04f, 0x04f, 0x04f, 0x04e, 0x04e, 0x04e, 0x04e, 0x04e, 0x04e, 0x04e, 0x04d, + 0x04d, 0x04d, 0x04d, 0x04d, 0x04d, 0x04d, 0x04c, 0x04c, 0x04c, 0x04c, 0x04c, 0x04c, + 0x04c, 0x04c, 0x04b, 0x04b, 0x04b, 0x04b, 0x04b, 0x04b, 0x04b, 0x04a, 0x04a, 0x04a, + 0x04a, 0x04a, 0x04a, 0x04a, 0x04a, 0x049, 0x049, 0x049, 0x049, 0x049, 0x049, 0x049, + 0x048, 0x048, 0x048, 0x048, 0x048, 0x048, 0x048, 0x048, 0x047, 0x047, 0x047, 0x047, + 0x047, 0x047, 0x047, 0x047, 0x046, 0x046, 0x046, 0x046, 0x046, 0x046, 0x046, 0x046, + 0x045, 0x045, 0x045, 0x045, 0x045, 0x045, 0x045, 0x045, 0x044, 0x044, 0x044, 0x044, + 0x044, 0x044, 0x044, 0x044, 0x043, 0x043, 0x043, 0x043, 0x043, 0x043, 0x043, 0x043, + 0x042, 0x042, 0x042, 0x042, 0x042, 0x042, 0x042, 0x042, 0x041, 0x041, 0x041, 0x041, + 0x041, 0x041, 0x041, 0x041, 0x041, 0x040, 0x040, 0x040, 0x040, 0x040, 0x040, 0x040, + 0x040, 0x040, 0x03f, 0x03f, 0x03f, 0x03f, 0x03f, 0x03f, 0x03f, 0x03f, 0x03e, 0x03e, + 0x03e, 0x03e, 0x03e, 0x03e, 0x03e, 0x03e, 0x03e, 0x03d, 0x03d, 0x03d, 0x03d, 0x03d, + 0x03d, 0x03d, 0x03d, 0x03d, 0x03c, 0x03c, 0x03c, 0x03c, 0x03c, 0x03c, 0x03c, 0x03c, + 0x03c, 0x03c, 0x03b, 0x03b, 0x03b, 0x03b, 0x03b, 0x03b, 0x03b, 0x03b, 0x03b, 0x03a, + 0x03a, 0x03a, 0x03a, 0x03a, 0x03a, 0x03a, 0x03a, 0x03a, 0x03a, 0x039, 0x039, 0x039, + 0x039, 0x039, 0x039, 0x039, 0x039, 0x039, 0x038, 0x038, 0x038, 0x038, 0x038, 0x038, + 0x038, 0x038, 0x038, 0x038, 0x037, 0x037, 0x037, 0x037, 0x037, 0x037, 0x037, 0x037, + 0x037, 0x037, 0x036, 0x036, 0x036, 0x036, 0x036, 0x036, 0x036, 0x036, 0x036, 0x036, + 0x036, 0x035, 0x035, 0x035, 0x035, 0x035, 0x035, 0x035, 0x035, 0x035, 0x035, 0x034, + 0x034, 0x034, 0x034, 0x034, 0x034, 0x034, 0x034, 0x034, 0x034, 0x034, 0x033, 0x033, + 0x033, 0x033, 0x033, 0x033, 0x033, 0x033, 0x033, 0x033, 0x033, 0x032, 0x032, 0x032, + 0x032, 0x032, 0x032, 0x032, 0x032, 0x032, 0x032, 0x032, 0x031, 0x031, 0x031, 0x031, + 0x031, 0x031, 0x031, 0x031, 0x031, 0x031, 0x031, 0x030, 0x030, 0x030, 0x030, 0x030, + 0x030, 0x030, 0x030, 0x030, 0x030, 0x030, 0x030, 0x02f, 0x02f, 0x02f, 0x02f, 0x02f, + 0x02f, 0x02f, 0x02f, 0x02f, 0x02f, 0x02f, 0x02e, 0x02e, 0x02e, 0x02e, 0x02e, 0x02e, + 0x02e, 0x02e, 0x02e, 0x02e, 0x02e, 0x02e, 0x02d, 0x02d, 0x02d, 0x02d, 0x02d, 0x02d, + 0x02d, 0x02d, 0x02d, 0x02d, 0x02d, 0x02d, 0x02d, 0x02c, 0x02c, 0x02c, 0x02c, 0x02c, + 0x02c, 0x02c, 0x02c, 0x02c, 0x02c, 0x02c, 0x02c, 0x02b, 0x02b, 0x02b, 0x02b, 0x02b, + 0x02b, 0x02b, 0x02b, 0x02b, 0x02b, 0x02b, 0x02b, 0x02b, 0x02a, 0x02a, 0x02a, 0x02a, + 0x02a, 0x02a, 0x02a, 0x02a, 0x02a, 0x02a, 0x02a, 0x02a, 0x02a, 0x029, 0x029, 0x029, + 0x029, 0x029, 0x029, 0x029, 0x029, 0x029, 0x029, 0x029, 0x029, 0x029, 0x029, 0x028, + 0x028, 0x028, 0x028, 0x028, 0x028, 0x028, 0x028, 0x028, 0x028, 0x028, 0x028, 0x028, + 0x028, 0x027, 0x027, 0x027, 0x027, 0x027, 0x027, 0x027, 0x027, 0x027, 0x027, 0x027, + 0x027, 0x027, 0x027, 0x026, 0x026, 0x026, 0x026, 0x026, 0x026, 0x026, 0x026, 0x026, + 0x026, 0x026, 0x026, 0x026, 0x026, 0x026, 0x025, 0x025, 0x025, 0x025, 0x025, 0x025, + 0x025, 0x025, 0x025, 0x025, 0x025, 0x025, 0x025, 0x025, 0x025, 0x024, 0x024, 0x024, + 0x024, 0x024, 0x024, 0x024, 0x024, 0x024, 0x024, 0x024, 0x024, 0x024, 0x024, 0x024, + 0x023, 0x023, 0x023, 0x023, 0x023, 0x023, 0x023, 0x023, 0x023, 0x023, 0x023, 0x023, + 0x023, 0x023, 0x023, 0x023, 0x022, 0x022, 0x022, 0x022, 0x022, 0x022, 0x022, 0x022, + 0x022, 0x022, 0x022, 0x022, 0x022, 0x022, 0x022, 0x022, 0x021, 0x021, 0x021, 0x021, + 0x021, 0x021, 0x021, 0x021, 0x021, 0x021, 0x021, 0x021, 0x021, 0x021, 0x021, 0x021, + 0x021, 0x020, 0x020, 0x020, 0x020, 0x020, 0x020, 0x020, 0x020, 0x020, 0x020, 0x020, + 0x020, 0x020, 0x020, 0x020, 0x020, 0x020, 0x01f, 0x01f, 0x01f, 0x01f, 0x01f, 0x01f, + 0x01f, 0x01f, 0x01f, 0x01f, 0x01f, 0x01f, 0x01f, 0x01f, 0x01f, 0x01f, 0x01f, 0x01f, + 0x01e, 0x01e, 0x01e, 0x01e, 0x01e, 0x01e, 0x01e, 0x01e, 0x01e, 0x01e, 0x01e, 0x01e, + 0x01e, 0x01e, 0x01e, 0x01e, 0x01e, 0x01e, 0x01e, 0x01d, 0x01d, 0x01d, 0x01d, 0x01d, + 0x01d, 0x01d, 0x01d, 0x01d, 0x01d, 0x01d, 0x01d, 0x01d, 0x01d, 0x01d, 0x01d, 0x01d, + 0x01d, 0x01d, 0x01c, 0x01c, 0x01c, 0x01c, 0x01c, 0x01c, 0x01c, 0x01c, 0x01c, 0x01c, + 0x01c, 0x01c, 0x01c, 0x01c, 0x01c, 0x01c, 0x01c, 0x01c, 0x01c, 0x01b, 0x01b, 0x01b, + 0x01b, 0x01b, 0x01b, 0x01b, 0x01b, 0x01b, 0x01b, 0x01b, 0x01b, 0x01b, 0x01b, 0x01b, + 0x01b, 0x01b, 0x01b, 0x01b, 0x01b, 0x01b, 0x01a, 0x01a, 0x01a, 0x01a, 0x01a, 0x01a, + 0x01a, 0x01a, 0x01a, 0x01a, 0x01a, 0x01a, 0x01a, 0x01a, 0x01a, 0x01a, 0x01a, 0x01a, + 0x01a, 0x01a, 0x01a, 0x019, 0x019, 0x019, 0x019, 0x019, 0x019, 0x019, 0x019, 0x019, + 0x019, 0x019, 0x019, 0x019, 0x019, 0x019, 0x019, 0x019, 0x019, 0x019, 0x019, 0x019, + 0x019, 0x018, 0x018, 0x018, 0x018, 0x018, 0x018, 0x018, 0x018, 0x018, 0x018, 0x018, + 0x018, 0x018, 0x018, 0x018, 0x018, 0x018, 0x018, 0x018, 0x018, 0x018, 0x018, 0x018, + 0x018, 0x017, 0x017, 0x017, 0x017, 0x017, 0x017, 0x017, 0x017, 0x017, 0x017, 0x017, + 0x017, 0x017, 0x017, 0x017, 0x017, 0x017, 0x017, 0x017, 0x017, 0x017, 0x017, 0x017, + 0x017, 0x016, 0x016, 0x016, 0x016, 0x016, 0x016, 0x016, 0x016, 0x016, 0x016, 0x016, + 0x016, 0x016, 0x016, 0x016, 0x016, 0x016, 0x016, 0x016, 0x016, 0x016, 0x016, 0x016, + 0x016, 0x016, 0x015, 0x015, 0x015, 0x015, 0x015, 0x015, 0x015, 0x015, 0x015, 0x015, + 0x015, 0x015, 0x015, 0x015, 0x015, 0x015, 0x015, 0x015, 0x015, 0x015, 0x015, 0x015, + 0x015, 0x015, 0x015, 0x015, 0x014, 0x014, 0x014, 0x014, 0x014, 0x014, 0x014, 0x014, + 0x014, 0x014, 0x014, 0x014, 0x014, 0x014, 0x014, 0x014, 0x014, 0x014, 0x014, 0x014, + 0x014, 0x014, 0x014, 0x014, 0x014, 0x014, 0x014, 0x014, 0x013, 0x013, 0x013, 0x013, + 0x013, 0x013, 0x013, 0x013, 0x013, 0x013, 0x013, 0x013, 0x013, 0x013, 0x013, 0x013, + 0x013, 0x013, 0x013, 0x013, 0x013, 0x013, 0x013, 0x013, 0x013, 0x013, 0x013, 0x013, + 0x013, 0x012, 0x012, 0x012, 0x012, 0x012, 0x012, 0x012, 0x012, 0x012, 0x012, 0x012, + 0x012, 0x012, 0x012, 0x012, 0x012, 0x012, 0x012, 0x012, 0x012, 0x012, 0x012, 0x012, + 0x012, 0x012, 0x012, 0x012, 0x012, 0x012, 0x012, 0x012, 0x011, 0x011, 0x011, 0x011, + 0x011, 0x011, 0x011, 0x011, 0x011, 0x011, 0x011, 0x011, 0x011, 0x011, 0x011, 0x011, + 0x011, 0x011, 0x011, 0x011, 0x011, 0x011, 0x011, 0x011, 0x011, 0x011, 0x011, 0x011, + 0x011, 0x011, 0x011, 0x011, 0x011, 0x010, 0x010, 0x010, 0x010, 0x010, 0x010, 0x010, + 0x010, 0x010, 0x010, 0x010, 0x010, 0x010, 0x010, 0x010, 0x010, 0x010, 0x010, 0x010, + 0x010, 0x010, 0x010, 0x010, 0x010, 0x010, 0x010, 0x010, 0x010, 0x010, 0x010, 0x010, + 0x010, 0x010, 0x010, 0x00f, 0x00f, 0x00f, 0x00f, 0x00f, 0x00f, 0x00f, 0x00f, 0x00f, + 0x00f, 0x00f, 0x00f, 0x00f, 0x00f, 0x00f, 0x00f, 0x00f, 0x00f, 0x00f, 0x00f, 0x00f +}; +static_assert(sizeof(centTableSSGSquare) == sizeof(uint16_t) * Note::ABS_PITCH_RANGE, +"Invalid SSG square cent table size."); + +const uint16_t centTableSSGTriangle[3072] = { + 0x077, 0x077, 0x077, 0x077, 0x076, 0x076, 0x076, 0x076, 0x076, 0x075, 0x075, 0x075, + 0x075, 0x074, 0x074, 0x074, 0x074, 0x074, 0x073, 0x073, 0x073, 0x073, 0x073, 0x072, + 0x072, 0x072, 0x072, 0x072, 0x071, 0x071, 0x071, 0x071, 0x071, 0x070, 0x070, 0x070, + 0x070, 0x070, 0x06f, 0x06f, 0x06f, 0x06f, 0x06f, 0x06e, 0x06e, 0x06e, 0x06e, 0x06e, + 0x06d, 0x06d, 0x06d, 0x06d, 0x06d, 0x06c, 0x06c, 0x06c, 0x06c, 0x06c, 0x06b, 0x06b, + 0x06b, 0x06b, 0x06b, 0x06a, 0x06a, 0x06a, 0x06a, 0x06a, 0x069, 0x069, 0x069, 0x069, + 0x069, 0x069, 0x068, 0x068, 0x068, 0x068, 0x068, 0x067, 0x067, 0x067, 0x067, 0x067, + 0x066, 0x066, 0x066, 0x066, 0x066, 0x066, 0x065, 0x065, 0x065, 0x065, 0x065, 0x064, + 0x064, 0x064, 0x064, 0x064, 0x064, 0x063, 0x063, 0x063, 0x063, 0x063, 0x062, 0x062, + 0x062, 0x062, 0x062, 0x062, 0x061, 0x061, 0x061, 0x061, 0x061, 0x061, 0x060, 0x060, + 0x060, 0x060, 0x060, 0x060, 0x05f, 0x05f, 0x05f, 0x05f, 0x05f, 0x05e, 0x05e, 0x05e, + 0x05e, 0x05e, 0x05e, 0x05d, 0x05d, 0x05d, 0x05d, 0x05d, 0x05d, 0x05c, 0x05c, 0x05c, + 0x05c, 0x05c, 0x05c, 0x05b, 0x05b, 0x05b, 0x05b, 0x05b, 0x05b, 0x05a, 0x05a, 0x05a, + 0x05a, 0x05a, 0x05a, 0x05a, 0x059, 0x059, 0x059, 0x059, 0x059, 0x059, 0x058, 0x058, + 0x058, 0x058, 0x058, 0x058, 0x057, 0x057, 0x057, 0x057, 0x057, 0x057, 0x056, 0x056, + 0x056, 0x056, 0x056, 0x056, 0x056, 0x055, 0x055, 0x055, 0x055, 0x055, 0x055, 0x054, + 0x054, 0x054, 0x054, 0x054, 0x054, 0x054, 0x053, 0x053, 0x053, 0x053, 0x053, 0x053, + 0x053, 0x052, 0x052, 0x052, 0x052, 0x052, 0x052, 0x051, 0x051, 0x051, 0x051, 0x051, + 0x051, 0x051, 0x050, 0x050, 0x050, 0x050, 0x050, 0x050, 0x050, 0x04f, 0x04f, 0x04f, + 0x04f, 0x04f, 0x04f, 0x04f, 0x04e, 0x04e, 0x04e, 0x04e, 0x04e, 0x04e, 0x04e, 0x04d, + 0x04d, 0x04d, 0x04d, 0x04d, 0x04d, 0x04d, 0x04c, 0x04c, 0x04c, 0x04c, 0x04c, 0x04c, + 0x04c, 0x04c, 0x04b, 0x04b, 0x04b, 0x04b, 0x04b, 0x04b, 0x04b, 0x04a, 0x04a, 0x04a, + 0x04a, 0x04a, 0x04a, 0x04a, 0x04a, 0x049, 0x049, 0x049, 0x049, 0x049, 0x049, 0x049, + 0x048, 0x048, 0x048, 0x048, 0x048, 0x048, 0x048, 0x048, 0x047, 0x047, 0x047, 0x047, + 0x047, 0x047, 0x047, 0x047, 0x046, 0x046, 0x046, 0x046, 0x046, 0x046, 0x046, 0x046, + 0x045, 0x045, 0x045, 0x045, 0x045, 0x045, 0x045, 0x045, 0x044, 0x044, 0x044, 0x044, + 0x044, 0x044, 0x044, 0x044, 0x043, 0x043, 0x043, 0x043, 0x043, 0x043, 0x043, 0x043, + 0x042, 0x042, 0x042, 0x042, 0x042, 0x042, 0x042, 0x042, 0x041, 0x041, 0x041, 0x041, + 0x041, 0x041, 0x041, 0x041, 0x041, 0x040, 0x040, 0x040, 0x040, 0x040, 0x040, 0x040, + 0x040, 0x040, 0x03f, 0x03f, 0x03f, 0x03f, 0x03f, 0x03f, 0x03f, 0x03f, 0x03e, 0x03e, + 0x03e, 0x03e, 0x03e, 0x03e, 0x03e, 0x03e, 0x03e, 0x03d, 0x03d, 0x03d, 0x03d, 0x03d, + 0x03d, 0x03d, 0x03d, 0x03d, 0x03c, 0x03c, 0x03c, 0x03c, 0x03c, 0x03c, 0x03c, 0x03c, + 0x03c, 0x03c, 0x03b, 0x03b, 0x03b, 0x03b, 0x03b, 0x03b, 0x03b, 0x03b, 0x03b, 0x03a, + 0x03a, 0x03a, 0x03a, 0x03a, 0x03a, 0x03a, 0x03a, 0x03a, 0x03a, 0x039, 0x039, 0x039, + 0x039, 0x039, 0x039, 0x039, 0x039, 0x039, 0x038, 0x038, 0x038, 0x038, 0x038, 0x038, + 0x038, 0x038, 0x038, 0x038, 0x037, 0x037, 0x037, 0x037, 0x037, 0x037, 0x037, 0x037, + 0x037, 0x037, 0x036, 0x036, 0x036, 0x036, 0x036, 0x036, 0x036, 0x036, 0x036, 0x036, + 0x036, 0x035, 0x035, 0x035, 0x035, 0x035, 0x035, 0x035, 0x035, 0x035, 0x035, 0x034, + 0x034, 0x034, 0x034, 0x034, 0x034, 0x034, 0x034, 0x034, 0x034, 0x034, 0x033, 0x033, + 0x033, 0x033, 0x033, 0x033, 0x033, 0x033, 0x033, 0x033, 0x033, 0x032, 0x032, 0x032, + 0x032, 0x032, 0x032, 0x032, 0x032, 0x032, 0x032, 0x032, 0x031, 0x031, 0x031, 0x031, + 0x031, 0x031, 0x031, 0x031, 0x031, 0x031, 0x031, 0x030, 0x030, 0x030, 0x030, 0x030, + 0x030, 0x030, 0x030, 0x030, 0x030, 0x030, 0x030, 0x02f, 0x02f, 0x02f, 0x02f, 0x02f, + 0x02f, 0x02f, 0x02f, 0x02f, 0x02f, 0x02f, 0x02e, 0x02e, 0x02e, 0x02e, 0x02e, 0x02e, + 0x02e, 0x02e, 0x02e, 0x02e, 0x02e, 0x02e, 0x02d, 0x02d, 0x02d, 0x02d, 0x02d, 0x02d, + 0x02d, 0x02d, 0x02d, 0x02d, 0x02d, 0x02d, 0x02d, 0x02c, 0x02c, 0x02c, 0x02c, 0x02c, + 0x02c, 0x02c, 0x02c, 0x02c, 0x02c, 0x02c, 0x02c, 0x02b, 0x02b, 0x02b, 0x02b, 0x02b, + 0x02b, 0x02b, 0x02b, 0x02b, 0x02b, 0x02b, 0x02b, 0x02b, 0x02a, 0x02a, 0x02a, 0x02a, + 0x02a, 0x02a, 0x02a, 0x02a, 0x02a, 0x02a, 0x02a, 0x02a, 0x02a, 0x029, 0x029, 0x029, + 0x029, 0x029, 0x029, 0x029, 0x029, 0x029, 0x029, 0x029, 0x029, 0x029, 0x029, 0x028, + 0x028, 0x028, 0x028, 0x028, 0x028, 0x028, 0x028, 0x028, 0x028, 0x028, 0x028, 0x028, + 0x028, 0x027, 0x027, 0x027, 0x027, 0x027, 0x027, 0x027, 0x027, 0x027, 0x027, 0x027, + 0x027, 0x027, 0x027, 0x026, 0x026, 0x026, 0x026, 0x026, 0x026, 0x026, 0x026, 0x026, + 0x026, 0x026, 0x026, 0x026, 0x026, 0x026, 0x025, 0x025, 0x025, 0x025, 0x025, 0x025, + 0x025, 0x025, 0x025, 0x025, 0x025, 0x025, 0x025, 0x025, 0x025, 0x024, 0x024, 0x024, + 0x024, 0x024, 0x024, 0x024, 0x024, 0x024, 0x024, 0x024, 0x024, 0x024, 0x024, 0x024, + 0x023, 0x023, 0x023, 0x023, 0x023, 0x023, 0x023, 0x023, 0x023, 0x023, 0x023, 0x023, + 0x023, 0x023, 0x023, 0x023, 0x022, 0x022, 0x022, 0x022, 0x022, 0x022, 0x022, 0x022, + 0x022, 0x022, 0x022, 0x022, 0x022, 0x022, 0x022, 0x022, 0x021, 0x021, 0x021, 0x021, + 0x021, 0x021, 0x021, 0x021, 0x021, 0x021, 0x021, 0x021, 0x021, 0x021, 0x021, 0x021, + 0x021, 0x020, 0x020, 0x020, 0x020, 0x020, 0x020, 0x020, 0x020, 0x020, 0x020, 0x020, + 0x020, 0x020, 0x020, 0x020, 0x020, 0x020, 0x01f, 0x01f, 0x01f, 0x01f, 0x01f, 0x01f, + 0x01f, 0x01f, 0x01f, 0x01f, 0x01f, 0x01f, 0x01f, 0x01f, 0x01f, 0x01f, 0x01f, 0x01f, + 0x01e, 0x01e, 0x01e, 0x01e, 0x01e, 0x01e, 0x01e, 0x01e, 0x01e, 0x01e, 0x01e, 0x01e, + 0x01e, 0x01e, 0x01e, 0x01e, 0x01e, 0x01e, 0x01e, 0x01d, 0x01d, 0x01d, 0x01d, 0x01d, + 0x01d, 0x01d, 0x01d, 0x01d, 0x01d, 0x01d, 0x01d, 0x01d, 0x01d, 0x01d, 0x01d, 0x01d, + 0x01d, 0x01d, 0x01c, 0x01c, 0x01c, 0x01c, 0x01c, 0x01c, 0x01c, 0x01c, 0x01c, 0x01c, + 0x01c, 0x01c, 0x01c, 0x01c, 0x01c, 0x01c, 0x01c, 0x01c, 0x01c, 0x01b, 0x01b, 0x01b, + 0x01b, 0x01b, 0x01b, 0x01b, 0x01b, 0x01b, 0x01b, 0x01b, 0x01b, 0x01b, 0x01b, 0x01b, + 0x01b, 0x01b, 0x01b, 0x01b, 0x01b, 0x01b, 0x01a, 0x01a, 0x01a, 0x01a, 0x01a, 0x01a, + 0x01a, 0x01a, 0x01a, 0x01a, 0x01a, 0x01a, 0x01a, 0x01a, 0x01a, 0x01a, 0x01a, 0x01a, + 0x01a, 0x01a, 0x01a, 0x019, 0x019, 0x019, 0x019, 0x019, 0x019, 0x019, 0x019, 0x019, + 0x019, 0x019, 0x019, 0x019, 0x019, 0x019, 0x019, 0x019, 0x019, 0x019, 0x019, 0x019, + 0x019, 0x018, 0x018, 0x018, 0x018, 0x018, 0x018, 0x018, 0x018, 0x018, 0x018, 0x018, + 0x018, 0x018, 0x018, 0x018, 0x018, 0x018, 0x018, 0x018, 0x018, 0x018, 0x018, 0x018, + 0x018, 0x017, 0x017, 0x017, 0x017, 0x017, 0x017, 0x017, 0x017, 0x017, 0x017, 0x017, + 0x017, 0x017, 0x017, 0x017, 0x017, 0x017, 0x017, 0x017, 0x017, 0x017, 0x017, 0x017, + 0x017, 0x016, 0x016, 0x016, 0x016, 0x016, 0x016, 0x016, 0x016, 0x016, 0x016, 0x016, + 0x016, 0x016, 0x016, 0x016, 0x016, 0x016, 0x016, 0x016, 0x016, 0x016, 0x016, 0x016, + 0x016, 0x016, 0x015, 0x015, 0x015, 0x015, 0x015, 0x015, 0x015, 0x015, 0x015, 0x015, + 0x015, 0x015, 0x015, 0x015, 0x015, 0x015, 0x015, 0x015, 0x015, 0x015, 0x015, 0x015, + 0x015, 0x015, 0x015, 0x015, 0x014, 0x014, 0x014, 0x014, 0x014, 0x014, 0x014, 0x014, + 0x014, 0x014, 0x014, 0x014, 0x014, 0x014, 0x014, 0x014, 0x014, 0x014, 0x014, 0x014, + 0x014, 0x014, 0x014, 0x014, 0x014, 0x014, 0x014, 0x014, 0x013, 0x013, 0x013, 0x013, + 0x013, 0x013, 0x013, 0x013, 0x013, 0x013, 0x013, 0x013, 0x013, 0x013, 0x013, 0x013, + 0x013, 0x013, 0x013, 0x013, 0x013, 0x013, 0x013, 0x013, 0x013, 0x013, 0x013, 0x013, + 0x013, 0x012, 0x012, 0x012, 0x012, 0x012, 0x012, 0x012, 0x012, 0x012, 0x012, 0x012, + 0x012, 0x012, 0x012, 0x012, 0x012, 0x012, 0x012, 0x012, 0x012, 0x012, 0x012, 0x012, + 0x012, 0x012, 0x012, 0x012, 0x012, 0x012, 0x012, 0x012, 0x011, 0x011, 0x011, 0x011, + 0x011, 0x011, 0x011, 0x011, 0x011, 0x011, 0x011, 0x011, 0x011, 0x011, 0x011, 0x011, + 0x011, 0x011, 0x011, 0x011, 0x011, 0x011, 0x011, 0x011, 0x011, 0x011, 0x011, 0x011, + 0x011, 0x011, 0x011, 0x011, 0x011, 0x010, 0x010, 0x010, 0x010, 0x010, 0x010, 0x010, + 0x010, 0x010, 0x010, 0x010, 0x010, 0x010, 0x010, 0x010, 0x010, 0x010, 0x010, 0x010, + 0x010, 0x010, 0x010, 0x010, 0x010, 0x010, 0x010, 0x010, 0x010, 0x010, 0x010, 0x010, + 0x010, 0x010, 0x010, 0x00f, 0x00f, 0x00f, 0x00f, 0x00f, 0x00f, 0x00f, 0x00f, 0x00f, + 0x00f, 0x00f, 0x00f, 0x00f, 0x00f, 0x00f, 0x00f, 0x00f, 0x00f, 0x00f, 0x00f, 0x00f, + 0x00f, 0x00f, 0x00f, 0x00f, 0x00f, 0x00f, 0x00f, 0x00f, 0x00f, 0x00f, 0x00f, 0x00f, + 0x00f, 0x00f, 0x00f, 0x00f, 0x00e, 0x00e, 0x00e, 0x00e, 0x00e, 0x00e, 0x00e, 0x00e, + 0x00e, 0x00e, 0x00e, 0x00e, 0x00e, 0x00e, 0x00e, 0x00e, 0x00e, 0x00e, 0x00e, 0x00e, + 0x00e, 0x00e, 0x00e, 0x00e, 0x00e, 0x00e, 0x00e, 0x00e, 0x00e, 0x00e, 0x00e, 0x00e, + 0x00e, 0x00e, 0x00e, 0x00e, 0x00e, 0x00e, 0x00e, 0x00e, 0x00d, 0x00d, 0x00d, 0x00d, + 0x00d, 0x00d, 0x00d, 0x00d, 0x00d, 0x00d, 0x00d, 0x00d, 0x00d, 0x00d, 0x00d, 0x00d, + 0x00d, 0x00d, 0x00d, 0x00d, 0x00d, 0x00d, 0x00d, 0x00d, 0x00d, 0x00d, 0x00d, 0x00d, + 0x00d, 0x00d, 0x00d, 0x00d, 0x00d, 0x00d, 0x00d, 0x00d, 0x00d, 0x00d, 0x00d, 0x00d, + 0x00d, 0x00d, 0x00d, 0x00c, 0x00c, 0x00c, 0x00c, 0x00c, 0x00c, 0x00c, 0x00c, 0x00c, + 0x00c, 0x00c, 0x00c, 0x00c, 0x00c, 0x00c, 0x00c, 0x00c, 0x00c, 0x00c, 0x00c, 0x00c, + 0x00c, 0x00c, 0x00c, 0x00c, 0x00c, 0x00c, 0x00c, 0x00c, 0x00c, 0x00c, 0x00c, 0x00c, + 0x00c, 0x00c, 0x00c, 0x00c, 0x00c, 0x00c, 0x00c, 0x00c, 0x00c, 0x00c, 0x00c, 0x00c, + 0x00c, 0x00b, 0x00b, 0x00b, 0x00b, 0x00b, 0x00b, 0x00b, 0x00b, 0x00b, 0x00b, 0x00b, + 0x00b, 0x00b, 0x00b, 0x00b, 0x00b, 0x00b, 0x00b, 0x00b, 0x00b, 0x00b, 0x00b, 0x00b, + 0x00b, 0x00b, 0x00b, 0x00b, 0x00b, 0x00b, 0x00b, 0x00b, 0x00b, 0x00b, 0x00b, 0x00b, + 0x00b, 0x00b, 0x00b, 0x00b, 0x00b, 0x00b, 0x00b, 0x00b, 0x00b, 0x00b, 0x00b, 0x00b, + 0x00b, 0x00b, 0x00b, 0x00b, 0x00a, 0x00a, 0x00a, 0x00a, 0x00a, 0x00a, 0x00a, 0x00a, + 0x00a, 0x00a, 0x00a, 0x00a, 0x00a, 0x00a, 0x00a, 0x00a, 0x00a, 0x00a, 0x00a, 0x00a, + 0x00a, 0x00a, 0x00a, 0x00a, 0x00a, 0x00a, 0x00a, 0x00a, 0x00a, 0x00a, 0x00a, 0x00a, + 0x00a, 0x00a, 0x00a, 0x00a, 0x00a, 0x00a, 0x00a, 0x00a, 0x00a, 0x00a, 0x00a, 0x00a, + 0x00a, 0x00a, 0x00a, 0x00a, 0x00a, 0x00a, 0x00a, 0x00a, 0x00a, 0x00a, 0x00a, 0x00a, + 0x009, 0x009, 0x009, 0x009, 0x009, 0x009, 0x009, 0x009, 0x009, 0x009, 0x009, 0x009, + 0x009, 0x009, 0x009, 0x009, 0x009, 0x009, 0x009, 0x009, 0x009, 0x009, 0x009, 0x009, + 0x009, 0x009, 0x009, 0x009, 0x009, 0x009, 0x009, 0x009, 0x009, 0x009, 0x009, 0x009, + 0x009, 0x009, 0x009, 0x009, 0x009, 0x009, 0x009, 0x009, 0x009, 0x009, 0x009, 0x009, + 0x009, 0x009, 0x009, 0x009, 0x009, 0x009, 0x009, 0x009, 0x009, 0x009, 0x009, 0x009, + 0x009, 0x009, 0x008, 0x008, 0x008, 0x008, 0x008, 0x008, 0x008, 0x008, 0x008, 0x008, + 0x008, 0x008, 0x008, 0x008, 0x008, 0x008, 0x008, 0x008, 0x008, 0x008, 0x008, 0x008, + 0x008, 0x008, 0x008, 0x008, 0x008, 0x008, 0x008, 0x008, 0x008, 0x008, 0x008, 0x008, + 0x008, 0x008, 0x008, 0x008, 0x008, 0x008, 0x008, 0x008, 0x008, 0x008, 0x008, 0x008, + 0x008, 0x008, 0x008, 0x008, 0x008, 0x008, 0x008, 0x008, 0x008, 0x008, 0x008, 0x008, + 0x008, 0x008, 0x008, 0x008, 0x008, 0x008, 0x008, 0x008, 0x008, 0x008, 0x008, 0x008, + 0x007, 0x007, 0x007, 0x007, 0x007, 0x007, 0x007, 0x007, 0x007, 0x007, 0x007, 0x007, + 0x007, 0x007, 0x007, 0x007, 0x007, 0x007, 0x007, 0x007, 0x007, 0x007, 0x007, 0x007, + 0x007, 0x007, 0x007, 0x007, 0x007, 0x007, 0x007, 0x007, 0x007, 0x007, 0x007, 0x007, + 0x007, 0x007, 0x007, 0x007, 0x007, 0x007, 0x007, 0x007, 0x007, 0x007, 0x007, 0x007, + 0x007, 0x007, 0x007, 0x007, 0x007, 0x007, 0x007, 0x007, 0x007, 0x007, 0x007, 0x007, + 0x007, 0x007, 0x007, 0x007, 0x007, 0x007, 0x007, 0x007, 0x007, 0x007, 0x007, 0x007, + 0x007, 0x007, 0x007, 0x007, 0x007, 0x007, 0x007, 0x007, 0x006, 0x006, 0x006, 0x006, + 0x006, 0x006, 0x006, 0x006, 0x006, 0x006, 0x006, 0x006, 0x006, 0x006, 0x006, 0x006, + 0x006, 0x006, 0x006, 0x006, 0x006, 0x006, 0x006, 0x006, 0x006, 0x006, 0x006, 0x006, + 0x006, 0x006, 0x006, 0x006, 0x006, 0x006, 0x006, 0x006, 0x006, 0x006, 0x006, 0x006, + 0x006, 0x006, 0x006, 0x006, 0x006, 0x006, 0x006, 0x006, 0x006, 0x006, 0x006, 0x006, + 0x006, 0x006, 0x006, 0x006, 0x006, 0x006, 0x006, 0x006, 0x006, 0x006, 0x006, 0x006, + 0x006, 0x006, 0x006, 0x006, 0x006, 0x006, 0x006, 0x006, 0x006, 0x006, 0x006, 0x006, + 0x006, 0x006, 0x006, 0x006, 0x006, 0x006, 0x006, 0x006, 0x006, 0x006, 0x006, 0x006, + 0x006, 0x006, 0x006, 0x006, 0x006, 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, + 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, + 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, + 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, + 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, + 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, + 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, + 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, + 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, + 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, 0x004, + 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, + 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, + 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, + 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, + 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, + 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, + 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, + 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, + 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, + 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, + 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, + 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x003, + 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, + 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, + 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, + 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, + 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, + 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, + 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, + 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, + 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, + 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, + 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, + 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, + 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, + 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, + 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, + 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, + 0x003, 0x003, 0x003, 0x003, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, + 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, + 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, + 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, + 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, + 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, + 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, + 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, + 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, + 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, + 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, + 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, + 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, + 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, + 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, + 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, + 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, + 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, + 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, + 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, + 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, + 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, + 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, + 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, + 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, + 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, + 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, + 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, + 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, + 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, + 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, + 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, + 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, + 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, + 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, + 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, + 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, + 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, + 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, + 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, + 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, + 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, + 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, + 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, + 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, + 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, + 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, + 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, + 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, + 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, + 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, + 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, + 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, + 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, + 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, + 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, + 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, + 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, + 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, + 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, + 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, + 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, + 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, + 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, + 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, + 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, + 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, + 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, + 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, + 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, + 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, + 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, + 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, + 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, + 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, + 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001 +}; +static_assert(sizeof(centTableSSGTriangle) == sizeof(uint16_t) * Note::ABS_PITCH_RANGE, +"Invalid SSG triangle cent table size."); + +const uint16_t centTableSSGSaw[3072] = { + 0x0ef, 0x0ee, 0x0ee, 0x0ed, 0x0ed, 0x0ec, 0x0ec, 0x0ec, 0x0eb, 0x0eb, 0x0ea, 0x0ea, + 0x0e9, 0x0e9, 0x0e9, 0x0e8, 0x0e8, 0x0e7, 0x0e7, 0x0e6, 0x0e6, 0x0e6, 0x0e5, 0x0e5, + 0x0e4, 0x0e4, 0x0e4, 0x0e3, 0x0e3, 0x0e2, 0x0e2, 0x0e2, 0x0e1, 0x0e1, 0x0e0, 0x0e0, + 0x0e0, 0x0df, 0x0df, 0x0de, 0x0de, 0x0dd, 0x0dd, 0x0dd, 0x0dc, 0x0dc, 0x0dc, 0x0db, + 0x0db, 0x0da, 0x0da, 0x0da, 0x0d9, 0x0d9, 0x0d8, 0x0d8, 0x0d8, 0x0d7, 0x0d7, 0x0d6, + 0x0d6, 0x0d6, 0x0d5, 0x0d5, 0x0d4, 0x0d4, 0x0d4, 0x0d3, 0x0d3, 0x0d3, 0x0d2, 0x0d2, + 0x0d1, 0x0d1, 0x0d1, 0x0d0, 0x0d0, 0x0d0, 0x0cf, 0x0cf, 0x0ce, 0x0ce, 0x0ce, 0x0cd, + 0x0cd, 0x0cd, 0x0cc, 0x0cc, 0x0cb, 0x0cb, 0x0cb, 0x0ca, 0x0ca, 0x0ca, 0x0c9, 0x0c9, + 0x0c9, 0x0c8, 0x0c8, 0x0c7, 0x0c7, 0x0c7, 0x0c6, 0x0c6, 0x0c6, 0x0c5, 0x0c5, 0x0c5, + 0x0c4, 0x0c4, 0x0c4, 0x0c3, 0x0c3, 0x0c3, 0x0c2, 0x0c2, 0x0c1, 0x0c1, 0x0c1, 0x0c0, + 0x0c0, 0x0c0, 0x0bf, 0x0bf, 0x0bf, 0x0be, 0x0be, 0x0be, 0x0bd, 0x0bd, 0x0bd, 0x0bc, + 0x0bc, 0x0bc, 0x0bb, 0x0bb, 0x0bb, 0x0ba, 0x0ba, 0x0ba, 0x0b9, 0x0b9, 0x0b9, 0x0b8, + 0x0b8, 0x0b8, 0x0b7, 0x0b7, 0x0b7, 0x0b6, 0x0b6, 0x0b6, 0x0b5, 0x0b5, 0x0b5, 0x0b4, + 0x0b4, 0x0b4, 0x0b3, 0x0b3, 0x0b3, 0x0b2, 0x0b2, 0x0b2, 0x0b1, 0x0b1, 0x0b1, 0x0b0, + 0x0b0, 0x0b0, 0x0af, 0x0af, 0x0af, 0x0af, 0x0ae, 0x0ae, 0x0ae, 0x0ad, 0x0ad, 0x0ad, + 0x0ac, 0x0ac, 0x0ac, 0x0ab, 0x0ab, 0x0ab, 0x0aa, 0x0aa, 0x0aa, 0x0aa, 0x0a9, 0x0a9, + 0x0a9, 0x0a8, 0x0a8, 0x0a8, 0x0a7, 0x0a7, 0x0a7, 0x0a7, 0x0a6, 0x0a6, 0x0a6, 0x0a5, + 0x0a5, 0x0a5, 0x0a4, 0x0a4, 0x0a4, 0x0a4, 0x0a3, 0x0a3, 0x0a3, 0x0a2, 0x0a2, 0x0a2, + 0x0a2, 0x0a1, 0x0a1, 0x0a1, 0x0a0, 0x0a0, 0x0a0, 0x09f, 0x09f, 0x09f, 0x09f, 0x09e, + 0x09e, 0x09e, 0x09d, 0x09d, 0x09d, 0x09d, 0x09c, 0x09c, 0x09c, 0x09b, 0x09b, 0x09b, + 0x09b, 0x09a, 0x09a, 0x09a, 0x09a, 0x099, 0x099, 0x099, 0x098, 0x098, 0x098, 0x098, + 0x097, 0x097, 0x097, 0x097, 0x096, 0x096, 0x096, 0x095, 0x095, 0x095, 0x095, 0x094, + 0x094, 0x094, 0x094, 0x093, 0x093, 0x093, 0x093, 0x092, 0x092, 0x092, 0x091, 0x091, + 0x091, 0x091, 0x090, 0x090, 0x090, 0x090, 0x08f, 0x08f, 0x08f, 0x08f, 0x08e, 0x08e, + 0x08e, 0x08e, 0x08d, 0x08d, 0x08d, 0x08d, 0x08c, 0x08c, 0x08c, 0x08c, 0x08b, 0x08b, + 0x08b, 0x08b, 0x08a, 0x08a, 0x08a, 0x08a, 0x089, 0x089, 0x089, 0x089, 0x088, 0x088, + 0x088, 0x088, 0x087, 0x087, 0x087, 0x087, 0x086, 0x086, 0x086, 0x086, 0x085, 0x085, + 0x085, 0x085, 0x084, 0x084, 0x084, 0x084, 0x083, 0x083, 0x083, 0x083, 0x083, 0x082, + 0x082, 0x082, 0x082, 0x081, 0x081, 0x081, 0x081, 0x080, 0x080, 0x080, 0x080, 0x07f, + 0x07f, 0x07f, 0x07f, 0x07f, 0x07e, 0x07e, 0x07e, 0x07e, 0x07d, 0x07d, 0x07d, 0x07d, + 0x07d, 0x07c, 0x07c, 0x07c, 0x07c, 0x07b, 0x07b, 0x07b, 0x07b, 0x07b, 0x07a, 0x07a, + 0x07a, 0x07a, 0x079, 0x079, 0x079, 0x079, 0x079, 0x078, 0x078, 0x078, 0x078, 0x077, + 0x077, 0x077, 0x077, 0x077, 0x076, 0x076, 0x076, 0x076, 0x076, 0x075, 0x075, 0x075, + 0x075, 0x074, 0x074, 0x074, 0x074, 0x074, 0x073, 0x073, 0x073, 0x073, 0x073, 0x072, + 0x072, 0x072, 0x072, 0x072, 0x071, 0x071, 0x071, 0x071, 0x071, 0x070, 0x070, 0x070, + 0x070, 0x070, 0x06f, 0x06f, 0x06f, 0x06f, 0x06f, 0x06e, 0x06e, 0x06e, 0x06e, 0x06e, + 0x06d, 0x06d, 0x06d, 0x06d, 0x06d, 0x06c, 0x06c, 0x06c, 0x06c, 0x06c, 0x06b, 0x06b, + 0x06b, 0x06b, 0x06b, 0x06a, 0x06a, 0x06a, 0x06a, 0x06a, 0x069, 0x069, 0x069, 0x069, + 0x069, 0x069, 0x068, 0x068, 0x068, 0x068, 0x068, 0x067, 0x067, 0x067, 0x067, 0x067, + 0x066, 0x066, 0x066, 0x066, 0x066, 0x066, 0x065, 0x065, 0x065, 0x065, 0x065, 0x064, + 0x064, 0x064, 0x064, 0x064, 0x064, 0x063, 0x063, 0x063, 0x063, 0x063, 0x062, 0x062, + 0x062, 0x062, 0x062, 0x062, 0x061, 0x061, 0x061, 0x061, 0x061, 0x061, 0x060, 0x060, + 0x060, 0x060, 0x060, 0x060, 0x05f, 0x05f, 0x05f, 0x05f, 0x05f, 0x05e, 0x05e, 0x05e, + 0x05e, 0x05e, 0x05e, 0x05d, 0x05d, 0x05d, 0x05d, 0x05d, 0x05d, 0x05c, 0x05c, 0x05c, + 0x05c, 0x05c, 0x05c, 0x05b, 0x05b, 0x05b, 0x05b, 0x05b, 0x05b, 0x05a, 0x05a, 0x05a, + 0x05a, 0x05a, 0x05a, 0x05a, 0x059, 0x059, 0x059, 0x059, 0x059, 0x059, 0x058, 0x058, + 0x058, 0x058, 0x058, 0x058, 0x057, 0x057, 0x057, 0x057, 0x057, 0x057, 0x056, 0x056, + 0x056, 0x056, 0x056, 0x056, 0x056, 0x055, 0x055, 0x055, 0x055, 0x055, 0x055, 0x054, + 0x054, 0x054, 0x054, 0x054, 0x054, 0x054, 0x053, 0x053, 0x053, 0x053, 0x053, 0x053, + 0x053, 0x052, 0x052, 0x052, 0x052, 0x052, 0x052, 0x051, 0x051, 0x051, 0x051, 0x051, + 0x051, 0x051, 0x050, 0x050, 0x050, 0x050, 0x050, 0x050, 0x050, 0x04f, 0x04f, 0x04f, + 0x04f, 0x04f, 0x04f, 0x04f, 0x04e, 0x04e, 0x04e, 0x04e, 0x04e, 0x04e, 0x04e, 0x04d, + 0x04d, 0x04d, 0x04d, 0x04d, 0x04d, 0x04d, 0x04c, 0x04c, 0x04c, 0x04c, 0x04c, 0x04c, + 0x04c, 0x04c, 0x04b, 0x04b, 0x04b, 0x04b, 0x04b, 0x04b, 0x04b, 0x04a, 0x04a, 0x04a, + 0x04a, 0x04a, 0x04a, 0x04a, 0x04a, 0x049, 0x049, 0x049, 0x049, 0x049, 0x049, 0x049, + 0x048, 0x048, 0x048, 0x048, 0x048, 0x048, 0x048, 0x048, 0x047, 0x047, 0x047, 0x047, + 0x047, 0x047, 0x047, 0x047, 0x046, 0x046, 0x046, 0x046, 0x046, 0x046, 0x046, 0x046, + 0x045, 0x045, 0x045, 0x045, 0x045, 0x045, 0x045, 0x045, 0x044, 0x044, 0x044, 0x044, + 0x044, 0x044, 0x044, 0x044, 0x043, 0x043, 0x043, 0x043, 0x043, 0x043, 0x043, 0x043, + 0x042, 0x042, 0x042, 0x042, 0x042, 0x042, 0x042, 0x042, 0x041, 0x041, 0x041, 0x041, + 0x041, 0x041, 0x041, 0x041, 0x041, 0x040, 0x040, 0x040, 0x040, 0x040, 0x040, 0x040, + 0x040, 0x040, 0x03f, 0x03f, 0x03f, 0x03f, 0x03f, 0x03f, 0x03f, 0x03f, 0x03e, 0x03e, + 0x03e, 0x03e, 0x03e, 0x03e, 0x03e, 0x03e, 0x03e, 0x03d, 0x03d, 0x03d, 0x03d, 0x03d, + 0x03d, 0x03d, 0x03d, 0x03d, 0x03c, 0x03c, 0x03c, 0x03c, 0x03c, 0x03c, 0x03c, 0x03c, + 0x03c, 0x03c, 0x03b, 0x03b, 0x03b, 0x03b, 0x03b, 0x03b, 0x03b, 0x03b, 0x03b, 0x03a, + 0x03a, 0x03a, 0x03a, 0x03a, 0x03a, 0x03a, 0x03a, 0x03a, 0x03a, 0x039, 0x039, 0x039, + 0x039, 0x039, 0x039, 0x039, 0x039, 0x039, 0x038, 0x038, 0x038, 0x038, 0x038, 0x038, + 0x038, 0x038, 0x038, 0x038, 0x037, 0x037, 0x037, 0x037, 0x037, 0x037, 0x037, 0x037, + 0x037, 0x037, 0x036, 0x036, 0x036, 0x036, 0x036, 0x036, 0x036, 0x036, 0x036, 0x036, + 0x036, 0x035, 0x035, 0x035, 0x035, 0x035, 0x035, 0x035, 0x035, 0x035, 0x035, 0x034, + 0x034, 0x034, 0x034, 0x034, 0x034, 0x034, 0x034, 0x034, 0x034, 0x034, 0x033, 0x033, + 0x033, 0x033, 0x033, 0x033, 0x033, 0x033, 0x033, 0x033, 0x033, 0x032, 0x032, 0x032, + 0x032, 0x032, 0x032, 0x032, 0x032, 0x032, 0x032, 0x032, 0x031, 0x031, 0x031, 0x031, + 0x031, 0x031, 0x031, 0x031, 0x031, 0x031, 0x031, 0x030, 0x030, 0x030, 0x030, 0x030, + 0x030, 0x030, 0x030, 0x030, 0x030, 0x030, 0x030, 0x02f, 0x02f, 0x02f, 0x02f, 0x02f, + 0x02f, 0x02f, 0x02f, 0x02f, 0x02f, 0x02f, 0x02e, 0x02e, 0x02e, 0x02e, 0x02e, 0x02e, + 0x02e, 0x02e, 0x02e, 0x02e, 0x02e, 0x02e, 0x02d, 0x02d, 0x02d, 0x02d, 0x02d, 0x02d, + 0x02d, 0x02d, 0x02d, 0x02d, 0x02d, 0x02d, 0x02d, 0x02c, 0x02c, 0x02c, 0x02c, 0x02c, + 0x02c, 0x02c, 0x02c, 0x02c, 0x02c, 0x02c, 0x02c, 0x02b, 0x02b, 0x02b, 0x02b, 0x02b, + 0x02b, 0x02b, 0x02b, 0x02b, 0x02b, 0x02b, 0x02b, 0x02b, 0x02a, 0x02a, 0x02a, 0x02a, + 0x02a, 0x02a, 0x02a, 0x02a, 0x02a, 0x02a, 0x02a, 0x02a, 0x02a, 0x029, 0x029, 0x029, + 0x029, 0x029, 0x029, 0x029, 0x029, 0x029, 0x029, 0x029, 0x029, 0x029, 0x029, 0x028, + 0x028, 0x028, 0x028, 0x028, 0x028, 0x028, 0x028, 0x028, 0x028, 0x028, 0x028, 0x028, + 0x028, 0x027, 0x027, 0x027, 0x027, 0x027, 0x027, 0x027, 0x027, 0x027, 0x027, 0x027, + 0x027, 0x027, 0x027, 0x026, 0x026, 0x026, 0x026, 0x026, 0x026, 0x026, 0x026, 0x026, + 0x026, 0x026, 0x026, 0x026, 0x026, 0x026, 0x025, 0x025, 0x025, 0x025, 0x025, 0x025, + 0x025, 0x025, 0x025, 0x025, 0x025, 0x025, 0x025, 0x025, 0x025, 0x024, 0x024, 0x024, + 0x024, 0x024, 0x024, 0x024, 0x024, 0x024, 0x024, 0x024, 0x024, 0x024, 0x024, 0x024, + 0x023, 0x023, 0x023, 0x023, 0x023, 0x023, 0x023, 0x023, 0x023, 0x023, 0x023, 0x023, + 0x023, 0x023, 0x023, 0x023, 0x022, 0x022, 0x022, 0x022, 0x022, 0x022, 0x022, 0x022, + 0x022, 0x022, 0x022, 0x022, 0x022, 0x022, 0x022, 0x022, 0x021, 0x021, 0x021, 0x021, + 0x021, 0x021, 0x021, 0x021, 0x021, 0x021, 0x021, 0x021, 0x021, 0x021, 0x021, 0x021, + 0x021, 0x020, 0x020, 0x020, 0x020, 0x020, 0x020, 0x020, 0x020, 0x020, 0x020, 0x020, + 0x020, 0x020, 0x020, 0x020, 0x020, 0x020, 0x01f, 0x01f, 0x01f, 0x01f, 0x01f, 0x01f, + 0x01f, 0x01f, 0x01f, 0x01f, 0x01f, 0x01f, 0x01f, 0x01f, 0x01f, 0x01f, 0x01f, 0x01f, + 0x01e, 0x01e, 0x01e, 0x01e, 0x01e, 0x01e, 0x01e, 0x01e, 0x01e, 0x01e, 0x01e, 0x01e, + 0x01e, 0x01e, 0x01e, 0x01e, 0x01e, 0x01e, 0x01e, 0x01d, 0x01d, 0x01d, 0x01d, 0x01d, + 0x01d, 0x01d, 0x01d, 0x01d, 0x01d, 0x01d, 0x01d, 0x01d, 0x01d, 0x01d, 0x01d, 0x01d, + 0x01d, 0x01d, 0x01c, 0x01c, 0x01c, 0x01c, 0x01c, 0x01c, 0x01c, 0x01c, 0x01c, 0x01c, + 0x01c, 0x01c, 0x01c, 0x01c, 0x01c, 0x01c, 0x01c, 0x01c, 0x01c, 0x01b, 0x01b, 0x01b, + 0x01b, 0x01b, 0x01b, 0x01b, 0x01b, 0x01b, 0x01b, 0x01b, 0x01b, 0x01b, 0x01b, 0x01b, + 0x01b, 0x01b, 0x01b, 0x01b, 0x01b, 0x01b, 0x01a, 0x01a, 0x01a, 0x01a, 0x01a, 0x01a, + 0x01a, 0x01a, 0x01a, 0x01a, 0x01a, 0x01a, 0x01a, 0x01a, 0x01a, 0x01a, 0x01a, 0x01a, + 0x01a, 0x01a, 0x01a, 0x019, 0x019, 0x019, 0x019, 0x019, 0x019, 0x019, 0x019, 0x019, + 0x019, 0x019, 0x019, 0x019, 0x019, 0x019, 0x019, 0x019, 0x019, 0x019, 0x019, 0x019, + 0x019, 0x018, 0x018, 0x018, 0x018, 0x018, 0x018, 0x018, 0x018, 0x018, 0x018, 0x018, + 0x018, 0x018, 0x018, 0x018, 0x018, 0x018, 0x018, 0x018, 0x018, 0x018, 0x018, 0x018, + 0x018, 0x017, 0x017, 0x017, 0x017, 0x017, 0x017, 0x017, 0x017, 0x017, 0x017, 0x017, + 0x017, 0x017, 0x017, 0x017, 0x017, 0x017, 0x017, 0x017, 0x017, 0x017, 0x017, 0x017, + 0x017, 0x016, 0x016, 0x016, 0x016, 0x016, 0x016, 0x016, 0x016, 0x016, 0x016, 0x016, + 0x016, 0x016, 0x016, 0x016, 0x016, 0x016, 0x016, 0x016, 0x016, 0x016, 0x016, 0x016, + 0x016, 0x016, 0x015, 0x015, 0x015, 0x015, 0x015, 0x015, 0x015, 0x015, 0x015, 0x015, + 0x015, 0x015, 0x015, 0x015, 0x015, 0x015, 0x015, 0x015, 0x015, 0x015, 0x015, 0x015, + 0x015, 0x015, 0x015, 0x015, 0x014, 0x014, 0x014, 0x014, 0x014, 0x014, 0x014, 0x014, + 0x014, 0x014, 0x014, 0x014, 0x014, 0x014, 0x014, 0x014, 0x014, 0x014, 0x014, 0x014, + 0x014, 0x014, 0x014, 0x014, 0x014, 0x014, 0x014, 0x014, 0x013, 0x013, 0x013, 0x013, + 0x013, 0x013, 0x013, 0x013, 0x013, 0x013, 0x013, 0x013, 0x013, 0x013, 0x013, 0x013, + 0x013, 0x013, 0x013, 0x013, 0x013, 0x013, 0x013, 0x013, 0x013, 0x013, 0x013, 0x013, + 0x013, 0x012, 0x012, 0x012, 0x012, 0x012, 0x012, 0x012, 0x012, 0x012, 0x012, 0x012, + 0x012, 0x012, 0x012, 0x012, 0x012, 0x012, 0x012, 0x012, 0x012, 0x012, 0x012, 0x012, + 0x012, 0x012, 0x012, 0x012, 0x012, 0x012, 0x012, 0x012, 0x011, 0x011, 0x011, 0x011, + 0x011, 0x011, 0x011, 0x011, 0x011, 0x011, 0x011, 0x011, 0x011, 0x011, 0x011, 0x011, + 0x011, 0x011, 0x011, 0x011, 0x011, 0x011, 0x011, 0x011, 0x011, 0x011, 0x011, 0x011, + 0x011, 0x011, 0x011, 0x011, 0x011, 0x010, 0x010, 0x010, 0x010, 0x010, 0x010, 0x010, + 0x010, 0x010, 0x010, 0x010, 0x010, 0x010, 0x010, 0x010, 0x010, 0x010, 0x010, 0x010, + 0x010, 0x010, 0x010, 0x010, 0x010, 0x010, 0x010, 0x010, 0x010, 0x010, 0x010, 0x010, + 0x010, 0x010, 0x010, 0x00f, 0x00f, 0x00f, 0x00f, 0x00f, 0x00f, 0x00f, 0x00f, 0x00f, + 0x00f, 0x00f, 0x00f, 0x00f, 0x00f, 0x00f, 0x00f, 0x00f, 0x00f, 0x00f, 0x00f, 0x00f, + 0x00f, 0x00f, 0x00f, 0x00f, 0x00f, 0x00f, 0x00f, 0x00f, 0x00f, 0x00f, 0x00f, 0x00f, + 0x00f, 0x00f, 0x00f, 0x00f, 0x00e, 0x00e, 0x00e, 0x00e, 0x00e, 0x00e, 0x00e, 0x00e, + 0x00e, 0x00e, 0x00e, 0x00e, 0x00e, 0x00e, 0x00e, 0x00e, 0x00e, 0x00e, 0x00e, 0x00e, + 0x00e, 0x00e, 0x00e, 0x00e, 0x00e, 0x00e, 0x00e, 0x00e, 0x00e, 0x00e, 0x00e, 0x00e, + 0x00e, 0x00e, 0x00e, 0x00e, 0x00e, 0x00e, 0x00e, 0x00e, 0x00d, 0x00d, 0x00d, 0x00d, + 0x00d, 0x00d, 0x00d, 0x00d, 0x00d, 0x00d, 0x00d, 0x00d, 0x00d, 0x00d, 0x00d, 0x00d, + 0x00d, 0x00d, 0x00d, 0x00d, 0x00d, 0x00d, 0x00d, 0x00d, 0x00d, 0x00d, 0x00d, 0x00d, + 0x00d, 0x00d, 0x00d, 0x00d, 0x00d, 0x00d, 0x00d, 0x00d, 0x00d, 0x00d, 0x00d, 0x00d, + 0x00d, 0x00d, 0x00d, 0x00c, 0x00c, 0x00c, 0x00c, 0x00c, 0x00c, 0x00c, 0x00c, 0x00c, + 0x00c, 0x00c, 0x00c, 0x00c, 0x00c, 0x00c, 0x00c, 0x00c, 0x00c, 0x00c, 0x00c, 0x00c, + 0x00c, 0x00c, 0x00c, 0x00c, 0x00c, 0x00c, 0x00c, 0x00c, 0x00c, 0x00c, 0x00c, 0x00c, + 0x00c, 0x00c, 0x00c, 0x00c, 0x00c, 0x00c, 0x00c, 0x00c, 0x00c, 0x00c, 0x00c, 0x00c, + 0x00c, 0x00b, 0x00b, 0x00b, 0x00b, 0x00b, 0x00b, 0x00b, 0x00b, 0x00b, 0x00b, 0x00b, + 0x00b, 0x00b, 0x00b, 0x00b, 0x00b, 0x00b, 0x00b, 0x00b, 0x00b, 0x00b, 0x00b, 0x00b, + 0x00b, 0x00b, 0x00b, 0x00b, 0x00b, 0x00b, 0x00b, 0x00b, 0x00b, 0x00b, 0x00b, 0x00b, + 0x00b, 0x00b, 0x00b, 0x00b, 0x00b, 0x00b, 0x00b, 0x00b, 0x00b, 0x00b, 0x00b, 0x00b, + 0x00b, 0x00b, 0x00b, 0x00b, 0x00a, 0x00a, 0x00a, 0x00a, 0x00a, 0x00a, 0x00a, 0x00a, + 0x00a, 0x00a, 0x00a, 0x00a, 0x00a, 0x00a, 0x00a, 0x00a, 0x00a, 0x00a, 0x00a, 0x00a, + 0x00a, 0x00a, 0x00a, 0x00a, 0x00a, 0x00a, 0x00a, 0x00a, 0x00a, 0x00a, 0x00a, 0x00a, + 0x00a, 0x00a, 0x00a, 0x00a, 0x00a, 0x00a, 0x00a, 0x00a, 0x00a, 0x00a, 0x00a, 0x00a, + 0x00a, 0x00a, 0x00a, 0x00a, 0x00a, 0x00a, 0x00a, 0x00a, 0x00a, 0x00a, 0x00a, 0x00a, + 0x009, 0x009, 0x009, 0x009, 0x009, 0x009, 0x009, 0x009, 0x009, 0x009, 0x009, 0x009, + 0x009, 0x009, 0x009, 0x009, 0x009, 0x009, 0x009, 0x009, 0x009, 0x009, 0x009, 0x009, + 0x009, 0x009, 0x009, 0x009, 0x009, 0x009, 0x009, 0x009, 0x009, 0x009, 0x009, 0x009, + 0x009, 0x009, 0x009, 0x009, 0x009, 0x009, 0x009, 0x009, 0x009, 0x009, 0x009, 0x009, + 0x009, 0x009, 0x009, 0x009, 0x009, 0x009, 0x009, 0x009, 0x009, 0x009, 0x009, 0x009, + 0x009, 0x009, 0x008, 0x008, 0x008, 0x008, 0x008, 0x008, 0x008, 0x008, 0x008, 0x008, + 0x008, 0x008, 0x008, 0x008, 0x008, 0x008, 0x008, 0x008, 0x008, 0x008, 0x008, 0x008, + 0x008, 0x008, 0x008, 0x008, 0x008, 0x008, 0x008, 0x008, 0x008, 0x008, 0x008, 0x008, + 0x008, 0x008, 0x008, 0x008, 0x008, 0x008, 0x008, 0x008, 0x008, 0x008, 0x008, 0x008, + 0x008, 0x008, 0x008, 0x008, 0x008, 0x008, 0x008, 0x008, 0x008, 0x008, 0x008, 0x008, + 0x008, 0x008, 0x008, 0x008, 0x008, 0x008, 0x008, 0x008, 0x008, 0x008, 0x008, 0x008, + 0x007, 0x007, 0x007, 0x007, 0x007, 0x007, 0x007, 0x007, 0x007, 0x007, 0x007, 0x007, + 0x007, 0x007, 0x007, 0x007, 0x007, 0x007, 0x007, 0x007, 0x007, 0x007, 0x007, 0x007, + 0x007, 0x007, 0x007, 0x007, 0x007, 0x007, 0x007, 0x007, 0x007, 0x007, 0x007, 0x007, + 0x007, 0x007, 0x007, 0x007, 0x007, 0x007, 0x007, 0x007, 0x007, 0x007, 0x007, 0x007, + 0x007, 0x007, 0x007, 0x007, 0x007, 0x007, 0x007, 0x007, 0x007, 0x007, 0x007, 0x007, + 0x007, 0x007, 0x007, 0x007, 0x007, 0x007, 0x007, 0x007, 0x007, 0x007, 0x007, 0x007, + 0x007, 0x007, 0x007, 0x007, 0x007, 0x007, 0x007, 0x007, 0x006, 0x006, 0x006, 0x006, + 0x006, 0x006, 0x006, 0x006, 0x006, 0x006, 0x006, 0x006, 0x006, 0x006, 0x006, 0x006, + 0x006, 0x006, 0x006, 0x006, 0x006, 0x006, 0x006, 0x006, 0x006, 0x006, 0x006, 0x006, + 0x006, 0x006, 0x006, 0x006, 0x006, 0x006, 0x006, 0x006, 0x006, 0x006, 0x006, 0x006, + 0x006, 0x006, 0x006, 0x006, 0x006, 0x006, 0x006, 0x006, 0x006, 0x006, 0x006, 0x006, + 0x006, 0x006, 0x006, 0x006, 0x006, 0x006, 0x006, 0x006, 0x006, 0x006, 0x006, 0x006, + 0x006, 0x006, 0x006, 0x006, 0x006, 0x006, 0x006, 0x006, 0x006, 0x006, 0x006, 0x006, + 0x006, 0x006, 0x006, 0x006, 0x006, 0x006, 0x006, 0x006, 0x006, 0x006, 0x006, 0x006, + 0x006, 0x006, 0x006, 0x006, 0x006, 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, + 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, + 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, + 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, + 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, + 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, + 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, + 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, + 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, + 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, 0x004, + 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, + 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, + 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, + 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, + 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, + 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, + 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, + 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, + 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, + 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, + 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, + 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x003, + 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, + 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, + 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, + 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, + 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, + 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, + 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, + 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, + 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, + 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, + 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, + 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, + 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, + 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, + 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, + 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, + 0x003, 0x003, 0x003, 0x003, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, + 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, + 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, + 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, + 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, + 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, + 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, + 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, + 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, + 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, + 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, + 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, + 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, + 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, + 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, + 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, + 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, + 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, + 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, + 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, + 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, + 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, + 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, + 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, + 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, + 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, + 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, + 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, + 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, + 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, + 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, + 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, + 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, + 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, + 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, + 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, + 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, + 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, + 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, + 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, + 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, + 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, + 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, + 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001 +}; +static_assert(sizeof(centTableSSGSaw) == sizeof(uint16_t) * Note::ABS_PITCH_RANGE, +"Invalid SSG saw cent table size."); +} + +uint16_t calculateFNumber(int absPitch, int finePitch) +{ + uint16_t p = centTableFM[absPitch]; + return (finePitch ? (finePitch > 0 ? p + finePitch : p - static_cast(-finePitch)) + : p); +} + +uint16_t calculateSSGSquareTP(int absPitch, int finePitch) +{ + uint16_t p = centTableSSGSquare[absPitch]; + return (finePitch ? (finePitch > 0 ? p - finePitch : p + static_cast(-finePitch)) + : p); +} + +uint16_t calculateSSGTriangleEP(int absPitch, int finePitch) +{ + uint16_t p = centTableSSGTriangle[absPitch]; + return (finePitch ? (finePitch > 0 ? p - finePitch : p + static_cast(-finePitch)) + : p); +} + +uint16_t calculateSSGSawEP(int absPitch, int finePitch) +{ + uint16_t p = centTableSSGSaw[absPitch]; + return (finePitch ? (finePitch > 0 ? p - finePitch : p + static_cast(-finePitch)) + : p); +} +} + +Note::Note(int noteNum) + : request_eval_(false) +{ + if (noteNum < 0) { + octave_ = 0; + name_ = NoteName::C; + pitch_ = 0; + return; + } + + octave_ = noteNum / 12; + if (octave_ > OCTAVE_RANGE - 1) { + octave_ = OCTAVE_RANGE - 1; + name_ = NoteName::B; + pitch_ = SEMINOTE_PITCH - 1; + return; + } + + name_ = static_cast(noteNum % 12); + pitch_ = 0; +} + +void Note::evaluateState(int octave, int note, int pitch) +{ + pitch_ = pitch % SEMINOTE_PITCH; + note += pitch / SEMINOTE_PITCH; + name_ = ((note % 12) + 12) % 12;; + octave_ = octave + std::floor(note / 12.); + if (octave_ < 0 || (!octave_ && !name_ && pitch_ < 0)) { + octave_ = 0; + name_ = NoteName::C; + pitch_ = 0; + } + else if (OCTAVE_RANGE <= octave_) { + octave_ = OCTAVE_RANGE - 1; + name_ = NoteName::B; + pitch_ = SEMINOTE_PITCH - 1; + } + request_eval_ = false; +} diff -Nru bambootracker-0.4.5+git20121209/BambooTracker/note.hpp bambootracker-0.4.6/BambooTracker/note.hpp --- bambootracker-0.4.5+git20121209/BambooTracker/note.hpp 1970-01-01 00:00:00.000000000 +0000 +++ bambootracker-0.4.6/BambooTracker/note.hpp 2021-02-11 14:37:17.000000000 +0000 @@ -0,0 +1,215 @@ +/* + * Copyright (C) 2018-2021 Rerrah + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +#pragma once + +#include +#include + +class Note; + +namespace note_utils +{ +uint16_t calculateFNumber(int absPitch, int finePitch); +uint16_t calculateSSGSquareTP(int absPitch, int finePitch); +uint16_t calculateSSGTriangleEP(int absPitch, int finePitch); +uint16_t calculateSSGSawEP(int absPitch, int finePitch); +} + +class Note +{ +public: + enum NoteName { C = 0, CS, D, DS, E, F, FS, G, GS, A, AS, B }; + + static constexpr int OCTAVE_RANGE = 8; + static constexpr int DEFAULT_OCTAVE = 4; + static constexpr int NOTE_NUMBER_RANGE = OCTAVE_RANGE * 12; // 96 + static constexpr int DEFAULT_NOTE_NUM = DEFAULT_OCTAVE * 12; // C4 + static constexpr int SEMINOTE_PITCH = 32; + static constexpr int ABS_PITCH_RANGE = NOTE_NUMBER_RANGE * SEMINOTE_PITCH; // 3072 + + Note(int octave, NoteName name, int pitch = 0); + explicit Note(int noteNum = DEFAULT_NOTE_NUM); + + void setOctave(int octave) noexcept; + int getOctave(); + + void setNoteName(NoteName name) noexcept; + NoteName getNotename(); + + void setPitch(int pitch) noexcept; + int getPitch(); + + int getAbsolutePicth(); + int getNoteNumber(); + + void add(const Note& note); + void add(int octave, NoteName name, int pitch = 0); + void addPitch(int pitch); + void addNoteNumber(int nn); + + friend bool operator==(Note& a, Note& b); + friend bool operator!=(Note& a, Note& b) { return !(a == b); } + + friend Note operator+(const Note& a, const Note& b); + friend Note operator+(const Note& a, int b); + friend Note operator+(int a, const Note& b); + + Note& operator+=(const Note& b); + Note& operator+=(int b); + +private: + int octave_; + int name_; + int pitch_; + bool request_eval_; + + Note(int octave, int name, int pitch); + void evaluateState(); + void evaluateState(int octave, int note, int pitch); +}; + +inline Note::Note(int octave, NoteName name, int pitch) + : octave_(octave), name_(name), pitch_(pitch), request_eval_(true) +{ +} + +inline Note::Note(int octave, int name, int pitch) + : octave_(octave), name_(name), pitch_(pitch), request_eval_(true) +{ +} + +inline void Note::setOctave(int octave) noexcept +{ + octave_ = octave; + request_eval_ = true; +} + +inline int Note::getOctave() +{ + if (request_eval_) evaluateState(); + return octave_; +} + +inline void Note::setNoteName(NoteName name) noexcept +{ + name_ = name; + request_eval_ = true; +} + +inline Note::NoteName Note::getNotename() +{ + if (request_eval_) evaluateState(); + return static_cast(name_); +} + +inline void Note::setPitch(int pitch) noexcept +{ + pitch_ = pitch; + request_eval_ = true; +} + +inline int Note::getPitch() +{ + if (request_eval_) evaluateState(); + return pitch_; +} + +inline int Note::getAbsolutePicth() +{ + if (request_eval_) evaluateState(); + return pitch_ + SEMINOTE_PITCH * (name_ + 12 * octave_); +} + +inline int Note::getNoteNumber() +{ + if (request_eval_) evaluateState(); + return 12 * octave_ + name_; +} + +inline void Note::add(const Note& note) +{ + add(note.octave_, static_cast(note.name_), note.pitch_); +} + +inline void Note::add(int octave, NoteName name, int pitch) +{ + octave_ += octave; + name_ += name; + pitch_ += pitch; + request_eval_ = true; +} + +inline void Note::addPitch(int pitch) +{ + pitch_ += pitch; + request_eval_ = true; +} + +inline void Note::addNoteNumber(int nn) +{ + name_ += nn; + request_eval_ = true; +} + +inline void Note::evaluateState() +{ + evaluateState(octave_, name_, pitch_); +} + +inline bool operator==(Note& a, Note& b) +{ + if (a.request_eval_) a.evaluateState(); + if (b.request_eval_) b.evaluateState(); + return (a.octave_ == b.octave_ && a.name_ == b.name_ && a.pitch_ == b.pitch_); +} + +inline Note operator+(const Note& a, const Note& b) +{ + return Note(a) += b; +} + +inline Note operator+(const Note& a, int b) +{ + return Note(a) += b; +} + +inline Note operator+(int a, const Note& b) +{ + return Note(b) += a; +} + +inline Note& Note::operator+=(const Note& b) +{ + add(b); + return *this; +} + +inline Note& Note::operator+=(int b) +{ + addPitch(b); + return *this; +} + diff -Nru bambootracker-0.4.5+git20121209/BambooTracker/opna_controller.cpp bambootracker-0.4.6/BambooTracker/opna_controller.cpp --- bambootracker-0.4.5+git20121209/BambooTracker/opna_controller.cpp 2020-12-08 14:05:28.000000000 +0000 +++ bambootracker-0.4.6/BambooTracker/opna_controller.cpp 2021-02-11 14:37:17.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (C) 2018-2020 Rerrah + * Copyright (C) 2018-2021 Rerrah * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation @@ -26,48 +26,55 @@ #include "opna_controller.hpp" #include #include -#include "calc_pitch.hpp" +#include "note.hpp" +#include "utils.hpp" + +namespace +{ +constexpr int UNUSED_VALUE = -1; + +const std::unordered_map> FM_ENV_PARAMS_OP = { + { FMOperatorType::All, { + FMEnvelopeParameter::AL, FMEnvelopeParameter::FB, + FMEnvelopeParameter::AR1, FMEnvelopeParameter::DR1, FMEnvelopeParameter::SR1, FMEnvelopeParameter::RR1, + FMEnvelopeParameter::SL1, FMEnvelopeParameter::TL1, FMEnvelopeParameter::KS1, FMEnvelopeParameter::ML1, + FMEnvelopeParameter::DT1, + FMEnvelopeParameter::AR2, FMEnvelopeParameter::DR2, FMEnvelopeParameter::SR2, FMEnvelopeParameter::RR2, + FMEnvelopeParameter::SL2, FMEnvelopeParameter::TL2, FMEnvelopeParameter::KS2, FMEnvelopeParameter::ML2, + FMEnvelopeParameter::DT2, + FMEnvelopeParameter::AR3, FMEnvelopeParameter::DR3, FMEnvelopeParameter::SR3, FMEnvelopeParameter::RR3, + FMEnvelopeParameter::SL3, FMEnvelopeParameter::TL3, FMEnvelopeParameter::KS3, FMEnvelopeParameter::ML3, + FMEnvelopeParameter::DT3, + FMEnvelopeParameter::AR4, FMEnvelopeParameter::DR4, FMEnvelopeParameter::SR4, FMEnvelopeParameter::RR4, + FMEnvelopeParameter::SL4, FMEnvelopeParameter::TL4, FMEnvelopeParameter::KS4, FMEnvelopeParameter::ML4, + FMEnvelopeParameter::DT4 + }}, + { FMOperatorType::Op1, { + FMEnvelopeParameter::AL, FMEnvelopeParameter::FB, + FMEnvelopeParameter::AR1, FMEnvelopeParameter::DR1, FMEnvelopeParameter::SR1, FMEnvelopeParameter::RR1, + FMEnvelopeParameter::SL1, FMEnvelopeParameter::TL1, FMEnvelopeParameter::KS1, FMEnvelopeParameter::ML1, + FMEnvelopeParameter::DT1 + }}, + { FMOperatorType::Op2, { + FMEnvelopeParameter::AR2, FMEnvelopeParameter::DR2, FMEnvelopeParameter::SR2, FMEnvelopeParameter::RR2, + FMEnvelopeParameter::SL2, FMEnvelopeParameter::TL2, FMEnvelopeParameter::KS2, FMEnvelopeParameter::ML2, + FMEnvelopeParameter::DT2 + }}, + { FMOperatorType::Op3, { + FMEnvelopeParameter::AR3, FMEnvelopeParameter::DR3, FMEnvelopeParameter::SR3, FMEnvelopeParameter::RR3, + FMEnvelopeParameter::SL3, FMEnvelopeParameter::TL3, FMEnvelopeParameter::KS3, FMEnvelopeParameter::ML3, + FMEnvelopeParameter::DT3 + }}, + { FMOperatorType::Op4, { + FMEnvelopeParameter::AR4, FMEnvelopeParameter::DR4, FMEnvelopeParameter::SR4, FMEnvelopeParameter::RR4, + FMEnvelopeParameter::SL4, FMEnvelopeParameter::TL4, FMEnvelopeParameter::KS4, FMEnvelopeParameter::ML4, + FMEnvelopeParameter::DT4 + }} +}; +} OPNAController::OPNAController(chip::OpnaEmulator emu, int clock, int rate, int duration) : mode_(SongType::Standard), - FM_ENV_PARAMS_OP_({ -{ FMOperatorType::All, { - FMEnvelopeParameter::AL, FMEnvelopeParameter::FB, - FMEnvelopeParameter::AR1, FMEnvelopeParameter::DR1, FMEnvelopeParameter::SR1, FMEnvelopeParameter::RR1, - FMEnvelopeParameter::SL1, FMEnvelopeParameter::TL1, FMEnvelopeParameter::KS1, FMEnvelopeParameter::ML1, - FMEnvelopeParameter::DT1, - FMEnvelopeParameter::AR2, FMEnvelopeParameter::DR2, FMEnvelopeParameter::SR2, FMEnvelopeParameter::RR2, - FMEnvelopeParameter::SL2, FMEnvelopeParameter::TL2, FMEnvelopeParameter::KS2, FMEnvelopeParameter::ML2, - FMEnvelopeParameter::DT2, - FMEnvelopeParameter::AR3, FMEnvelopeParameter::DR3, FMEnvelopeParameter::SR3, FMEnvelopeParameter::RR3, - FMEnvelopeParameter::SL3, FMEnvelopeParameter::TL3, FMEnvelopeParameter::KS3, FMEnvelopeParameter::ML3, - FMEnvelopeParameter::DT3, - FMEnvelopeParameter::AR4, FMEnvelopeParameter::DR4, FMEnvelopeParameter::SR4, FMEnvelopeParameter::RR4, - FMEnvelopeParameter::SL4, FMEnvelopeParameter::TL4, FMEnvelopeParameter::KS4, FMEnvelopeParameter::ML4, - FMEnvelopeParameter::DT4 - }}, -{ FMOperatorType::Op1, { - FMEnvelopeParameter::AL, FMEnvelopeParameter::FB, - FMEnvelopeParameter::AR1, FMEnvelopeParameter::DR1, FMEnvelopeParameter::SR1, FMEnvelopeParameter::RR1, - FMEnvelopeParameter::SL1, FMEnvelopeParameter::TL1, FMEnvelopeParameter::KS1, FMEnvelopeParameter::ML1, - FMEnvelopeParameter::DT1 - }}, -{ FMOperatorType::Op2, { - FMEnvelopeParameter::AR2, FMEnvelopeParameter::DR2, FMEnvelopeParameter::SR2, FMEnvelopeParameter::RR2, - FMEnvelopeParameter::SL2, FMEnvelopeParameter::TL2, FMEnvelopeParameter::KS2, FMEnvelopeParameter::ML2, - FMEnvelopeParameter::DT2 - }}, -{ FMOperatorType::Op3, { - FMEnvelopeParameter::AR3, FMEnvelopeParameter::DR3, FMEnvelopeParameter::SR3, FMEnvelopeParameter::RR3, - FMEnvelopeParameter::SL3, FMEnvelopeParameter::TL3, FMEnvelopeParameter::KS3, FMEnvelopeParameter::ML3, - FMEnvelopeParameter::DT3 - }}, -{ FMOperatorType::Op4, { - FMEnvelopeParameter::AR4, FMEnvelopeParameter::DR4, FMEnvelopeParameter::SR4, FMEnvelopeParameter::RR4, - FMEnvelopeParameter::SL4, FMEnvelopeParameter::TL4, FMEnvelopeParameter::KS4, FMEnvelopeParameter::ML4, - FMEnvelopeParameter::DT4 - }} - }), storePointADPCM_(0) { constexpr size_t DRAM_SIZE = 262144; // 256KiB @@ -75,27 +82,20 @@ std::make_unique(), std::make_unique()); - for (int ch = 0; ch < 6; ++ch) { - fmOpEnables_[ch] = 0xf; - isMuteFM_[ch] = false; - for (auto ep : FM_ENV_PARAMS_OP_.at(FMOperatorType::All)) - opSeqItFM_[ch].emplace(ep, nullptr); - } - - for (int ch = 0; ch < 3; ++ch) { - isMuteSSG_[ch] = false; - } - - for (int ch = 0; ch < 6; ++ch) { - isMuteRhythm_[ch] = false; - } - + for (size_t inch = 0; inch < 6; ++inch) { + fmOpEnables_[inch] = 0xf; + for (auto ep : FM_ENV_PARAMS_OP.at(FMOperatorType::All)) + opSeqItFM_[inch].emplace(ep, nullptr); + } + for (auto& fm : fm_) fm.isMute = false; + for (auto& ssg : ssg_) ssg.isMute = false; + for (auto& rhy : rhythm_) rhy.isMute = false; isMuteADPCM_ = false; - initChip(); + resetState(); - outputHistory_.reset(new int16_t[2 * OUTPUT_HISTORY_SIZE]{}); - outputHistoryReady_.reset(new int16_t[2 * OUTPUT_HISTORY_SIZE]{}); + outputHistory_.reset(new int16_t[2 * bt_defs::OUTPUT_HISTORY_SIZE]{}); + outputHistoryReady_.reset(new int16_t[2 * bt_defs::OUTPUT_HISTORY_SIZE]{}); outputHistoryIndex_ = 0; } @@ -103,12 +103,12 @@ void OPNAController::reset() { opna_->reset(); - initChip(); - std::fill(&outputHistory_[0], &outputHistory_[2 * OUTPUT_HISTORY_SIZE], 0); - std::fill(&outputHistoryReady_[0], &outputHistoryReady_[2 * OUTPUT_HISTORY_SIZE], 0); + resetState(); + std::fill(&outputHistory_[0], &outputHistory_[2 * bt_defs::OUTPUT_HISTORY_SIZE], 0); + std::fill(&outputHistoryReady_[0], &outputHistoryReady_[2 * bt_defs::OUTPUT_HISTORY_SIZE], 0); } -void OPNAController::initChip() +void OPNAController::resetState() { opna_->setRegister(0x29, 0x80); // Init interrupt / YM2608 mode @@ -124,8 +124,8 @@ void OPNAController::tickEvent(SoundSource src, int ch) { switch (src) { - case SoundSource::FM: tickEventFM(ch); break; - case SoundSource::SSG: tickEventSSG(ch); break; + case SoundSource::FM: tickEventFM(fm_[ch]); break; + case SoundSource::SSG: tickEventSSG(ssg_[ch]); break; case SoundSource::RHYTHM: break; case SoundSource::ADPCM: tickEventADPCM(); break; } @@ -183,7 +183,7 @@ { opna_->mix(container, nSamples); - size_t nHistory = std::min(nSamples, OUTPUT_HISTORY_SIZE); + size_t nHistory = std::min(nSamples, bt_defs::OUTPUT_HISTORY_SIZE); fillOutputHistory(&container[2 * (nSamples - nHistory)], nHistory); } @@ -191,7 +191,7 @@ { std::lock_guard lock(outputHistoryReadyMutex_); int16_t *history = outputHistoryReady_.get(); - std::copy(history, &history[2 * OUTPUT_HISTORY_SIZE], container); + std::copy(history, &history[2 * bt_defs::OUTPUT_HISTORY_SIZE], container); } void OPNAController::fillOutputHistory(const int16_t* outputs, size_t nSamples) @@ -200,7 +200,7 @@ size_t historyIndex = outputHistoryIndex_; // copy as many as possible to the back - size_t backCapacity = OUTPUT_HISTORY_SIZE - historyIndex; + size_t backCapacity = bt_defs::OUTPUT_HISTORY_SIZE - historyIndex; size_t nBack = std::min(nSamples, backCapacity); std::copy(outputs, &outputs[2 * nBack], &history[2 * historyIndex]); @@ -208,24 +208,18 @@ std::copy(&outputs[2 * nBack], &outputs[2 * nSamples], history); // update the write position - historyIndex = (historyIndex + nSamples) % OUTPUT_HISTORY_SIZE; + historyIndex = (historyIndex + nSamples) % bt_defs::OUTPUT_HISTORY_SIZE; outputHistoryIndex_ = historyIndex; // if no one holds the ready buffer, update the contents std::unique_lock lock(outputHistoryReadyMutex_, std::try_to_lock); - if (lock.owns_lock()) - transferReadyHistory(); -} - -void OPNAController::transferReadyHistory() -{ - const int16_t* src = outputHistory_.get(); - int16_t* dst = outputHistoryReady_.get(); - size_t index = outputHistoryIndex_; + if (lock.owns_lock()) { + int16_t* dst = outputHistoryReady_.get(); - // copy the back, and then the front - std::copy(&src[2 * index], &src[2 * OUTPUT_HISTORY_SIZE], dst); - std::copy(&src[0], &src[2 * index], &dst[2 * (OUTPUT_HISTORY_SIZE - index)]); + // copy the back, and then the front + std::copy(&history[2 * historyIndex], &history[2 * bt_defs::OUTPUT_HISTORY_SIZE], dst); + std::copy(&history[0], &history[2 * historyIndex], &dst[2 * (bt_defs::OUTPUT_HISTORY_SIZE - historyIndex)]); + } } /********** Chip mode **********/ @@ -235,19 +229,14 @@ reset(); } -SongType OPNAController::getMode() const -{ - return mode_; -} - /********** Mute **********/ void OPNAController::setMuteState(SoundSource src, int chInSrc, bool isMute) { switch (src) { - case SoundSource::FM: setMuteFMState(chInSrc, isMute); break; - case SoundSource::SSG: setMuteSSGState(chInSrc, isMute); break; + case SoundSource::FM: setMuteFMState(chInSrc, isMute); break; + case SoundSource::SSG: setMuteSSGState(chInSrc, isMute); break; case SoundSource::RHYTHM: setMuteRhythmState(chInSrc, isMute); break; - case SoundSource::ADPCM: setMuteADPCMState(isMute); break; + case SoundSource::ADPCM: setMuteADPCMState(isMute); break; } } @@ -293,142 +282,262 @@ opna_->setRegisterWriteLogger(cntr); } -/********** Internal process **********/ -void OPNAController::checkRealToneByArpeggio(int seqPos, - const std::unique_ptr& arpIt, - const std::deque& baseTone, ToneDetail& keyTone, - bool& needToneSet) +/********** Internal common process **********/ +void OPNAController::checkRealToneByArpeggio(const ArpeggioIterInterface& arpItr, + const EchoBuffer& echoBuf, Note& baseNote, + bool& shouldSetTone) { - if (seqPos == -1) return; + if (arpItr->hasEnded()) return; - switch (arpIt->getSequenceType()) { - case SequenceType::ABSOLUTE_SEQUENCE: + switch (arpItr->type()) { + case SequenceType::AbsoluteSequence: { - std::pair pair = noteNumberToOctaveAndNote( - octaveAndNoteToNoteNumber(baseTone.front().octave, - baseTone.front().note) - + arpIt->getCommandType() - 48); - keyTone.octave = pair.first; - keyTone.note = pair.second; + Note ln = echoBuf.latest(); + ln.addNoteNumber(arpItr->data().data - Note::DEFAULT_NOTE_NUM); + baseNote = std::move(ln); break; } - case SequenceType::FIXED_SEQUENCE: + case SequenceType::FixedSequence: { - std::pair pair = noteNumberToOctaveAndNote(arpIt->getCommandType()); - keyTone.octave = pair.first; - keyTone.note = pair.second; + baseNote = Note(arpItr->data().data); break; } - case SequenceType::RELATIVE_SEQUENCE: // Relative + case SequenceType::RelativeSequence: { - std::pair pair = noteNumberToOctaveAndNote( - octaveAndNoteToNoteNumber(keyTone.octave, keyTone.note) - + arpIt->getCommandType() - 48); - keyTone.octave = pair.first; - keyTone.note = pair.second; + baseNote.addNoteNumber(arpItr->data().data - Note::DEFAULT_NOTE_NUM); break; } default: return ; } - needToneSet = true; + shouldSetTone = true; } -void OPNAController::checkPortamento(const std::unique_ptr& arpIt, +void OPNAController::checkPortamento(const ArpeggioIterInterface& arpItr, int prtm, bool hasKeyOnBefore, bool isTonePrtm, - const std::deque& baseTone, - ToneDetail& keyTone, bool& needToneSet) + EchoBuffer& echoBuf, + Note& baseNote, bool& shouldSetTone) { - if ((!arpIt || arpIt->getPosition() == -1) && prtm && hasKeyOnBefore) { + if ((!arpItr || arpItr->hasEnded()) && prtm && hasKeyOnBefore) { if (isTonePrtm) { - int dif = ( octaveAndNoteToNoteNumber(baseTone.front().octave, baseTone.front().note) - * calc_pitch::SEMINOTE_PITCH + baseTone.front().pitch ) - - ( octaveAndNoteToNoteNumber(keyTone.octave, keyTone.note) - * calc_pitch::SEMINOTE_PITCH + keyTone.pitch ); + Note bufNote = echoBuf.latest(); + int dif = bufNote.getAbsolutePicth() - baseNote.getAbsolutePicth(); if (dif > 0) { - if (dif - prtm < 0) { - keyTone = baseTone.front(); - } - else { - keyTone.pitch += prtm; - } - needToneSet = true; + baseNote += std::min(dif, prtm); + shouldSetTone = true; } else if (dif < 0) { - if (dif + prtm > 0) { - keyTone = baseTone.front(); - } - else { - keyTone.pitch -= prtm; - } - needToneSet = true; + baseNote += std::max(dif, -prtm); + shouldSetTone = true; } } else { - keyTone.pitch += prtm; - needToneSet = true; + baseNote += prtm; + shouldSetTone = true; } } } -void OPNAController::checkRealToneByPitch(int seqPos, const std::unique_ptr& ptIt, - int& sumPitch, bool& needToneSet) +void OPNAController::checkRealToneByPitch(const std::unique_ptr::Iterator>& ptItr, + int& sumPitch, bool& shouldSetTone) { - if (seqPos == -1) return; + if (ptItr->hasEnded()) return; - switch (ptIt->getSequenceType()) { - case SequenceType::ABSOLUTE_SEQUENCE: - sumPitch = ptIt->getCommandType() - 127; + switch (ptItr->type()) { + case SequenceType::AbsoluteSequence: + sumPitch = ptItr->data().data - SEQ_PITCH_CENTER; break; - case SequenceType::RELATIVE_SEQUENCE: - sumPitch += (ptIt->getCommandType() - 127); + case SequenceType::RelativeSequence: + sumPitch += (ptItr->data().data - SEQ_PITCH_CENTER); break; default: return; } - needToneSet = true; + shouldSetTone = true; } //---------- FM ----------// +namespace +{ +/// IS_CARRIER[operator][algorithm] +constexpr bool IS_CARRIER[4][8] = { + { false, false, false, false, false, false, false, true }, + { false, false, false, false, true, true, true, true }, + { false, false, false, false, false, true, true, true }, + { true, true, true, true, true, true, true, true }, +}; + +constexpr uint8_t FM_KEYOFF_MASK[6] = { 0, 1, 2, 4, 5, 6 }; +const std::unordered_map FM3_KEY_OFF_MASK = { + { 2, 0xe }, { 6, 0xd }, { 7, 0xb }, { 8, 0x7 } +}; + +const std::unordered_map FM3_CH_TO_OP_NUM = { + { 2, 0u }, { 6, 1u }, { 7, 2u }, { 8, 3u } +}; +constexpr int FM3_OP_NUM_TO_CH[4] = { 2, 6, 7, 8 }; + +constexpr FMEnvelopeParameter PARAM_DT[4] = { + FMEnvelopeParameter::DT1, FMEnvelopeParameter::DT2, FMEnvelopeParameter::DT3, FMEnvelopeParameter::DT4 +}; +constexpr FMEnvelopeParameter PARAM_TL[4] = { + FMEnvelopeParameter::TL1, FMEnvelopeParameter::TL2, FMEnvelopeParameter::TL3, FMEnvelopeParameter::TL4 +}; +constexpr FMEnvelopeParameter PARAM_ML[4] = { + FMEnvelopeParameter::ML1, FMEnvelopeParameter::ML2, FMEnvelopeParameter::ML3, FMEnvelopeParameter::ML4 +}; +constexpr FMEnvelopeParameter PARAM_KS[4] = { + FMEnvelopeParameter::KS1, FMEnvelopeParameter::KS2, FMEnvelopeParameter::KS3, FMEnvelopeParameter::KS4 +}; +constexpr FMEnvelopeParameter PARAM_AR[4] = { + FMEnvelopeParameter::AR1, FMEnvelopeParameter::AR2, FMEnvelopeParameter::AR3, FMEnvelopeParameter::AR4 +}; +constexpr FMEnvelopeParameter PARAM_DR[4] = { + FMEnvelopeParameter::DR1, FMEnvelopeParameter::DR2, FMEnvelopeParameter::DR3, FMEnvelopeParameter::DR4 +}; +constexpr FMEnvelopeParameter PARAM_SR[4] = { + FMEnvelopeParameter::SR1, FMEnvelopeParameter::SR2, FMEnvelopeParameter::SR3, FMEnvelopeParameter::SR4 +}; +constexpr FMEnvelopeParameter PARAM_SL[4] = { + FMEnvelopeParameter::SL1, FMEnvelopeParameter::SL2, FMEnvelopeParameter::SL3, FMEnvelopeParameter::SL4 +}; +constexpr FMEnvelopeParameter PARAM_RR[4] = { + FMEnvelopeParameter::RR1, FMEnvelopeParameter::RR2, FMEnvelopeParameter::RR3, FMEnvelopeParameter::RR4 +}; +constexpr FMEnvelopeParameter PARAM_SSGEG[4] = { + FMEnvelopeParameter::SSGEG1, FMEnvelopeParameter::SSGEG2, FMEnvelopeParameter::SSGEG3, FMEnvelopeParameter::SSGEG4 +}; +constexpr FMLFOParameter PARAM_AM[4] = { + FMLFOParameter::AM1, FMLFOParameter::AM2, FMLFOParameter::AM3, FMLFOParameter::AM4 +}; + +constexpr uint32_t OP_OFFSET[4] = { 0u, 8u, 4u, 12u }; + +std::vector getOperatorsInLevel(int level, int al) +{ + switch (level) { + case 0: + switch (al) { + case 0: + case 1: + case 2: + case 3: + return { 3 }; + case 4: + return { 1, 3 }; + case 5: + case 6: + return { 1, 2, 3 }; + case 7: + return { 0, 1, 2, 3 }; + default: + throw std::invalid_argument("Invalid algorithm."); + } + case 1: + switch (al) { + case 0: + case 1: + return { 2 }; + case 2: + return { 0, 2 }; + case 3: + return { 1, 2 }; + case 4: + return { 0, 2 }; + case 5: + case 6: + return { 0 }; + case 7: + return {}; + default: + throw std::invalid_argument("Invalid algorithm."); + } + case 2: + switch (al) { + case 0: + return { 1 }; + case 1: + return { 0, 1 }; + case 2: + return { 1 }; + case 3: + return { 0 }; + case 4: + case 5: + case 6: + case 7: + return {}; + default: + throw std::invalid_argument("Invalid algorithm."); + } + case 3: + switch (al) { + case 0: + return { 0 }; + case 1: + case 2: + case 3: + case 4: + case 5: + case 6: + case 7: + return {}; + default: + throw std::invalid_argument("Invalid algorithm."); + } + default: + throw std::invalid_argument("Invalid operator level."); + } +} + +inline uint8_t judgeSSGEGRegisterValue(int v) +{ + return (v == -1) ? 0 : (0x08 + static_cast(v)); +} +} + /********** Key on-off **********/ -void OPNAController::keyOnFM(int ch, Note note, int octave, int pitch, bool isJam) +void OPNAController::keyOnFM(int ch, const Note& note, bool isJam) { - if (isMuteFM_[ch]) return; + auto& fm = fm_[ch]; + + if (fm.isMute) return; - updateEchoBufferFM(ch, octave, note, pitch); + fm.echoBuf.push(note); - bool isTonePrtm = isTonePrtmFM_[ch] && hasKeyOnBeforeFM_[ch]; + bool isTonePrtm = fm.isTonePrtm && fm.hasKeyOnBefore; if (isTonePrtm) { - keyToneFM_[ch].pitch += (sumNoteSldFM_[ch] + transposeFM_[ch]); + fm.baseNote += (fm.nsSum + fm.transpose); } else { - keyToneFM_[ch] = baseToneFM_[ch].front(); - sumPitchFM_[ch] = 0; - sumVolSldFM_[ch] = 0; - } - if (tmpVolFM_[ch] != -1) { - setVolumeFM(ch, baseVolFM_[ch]); - } - if (!noteSldFMSetFlag_[ch]) { - nsItFM_[ch].reset(); - } - noteSldFMSetFlag_[ch] = false; - needToneSetFM_[ch] = true; - sumNoteSldFM_[ch] = 0; - transposeFM_[ch] = 0; + fm.baseNote = fm.echoBuf.latest(); + fm.neverSetBaseNote = false; + fm.ptSum = 0; + fm.volSldSum = 0; + } + if (fm.oneshotVol != UNUSED_VALUE) { + setVolumeFM(ch, fm.baseVol); + } + if (!fm.hasSetNs) { + fm.nsItr.reset(); + } + fm.hasSetNs = false; + fm.shouldSetTone = true; + fm.nsSum = 0; + fm.transpose = 0; - setFrontFMSequences(ch); - hasPreSetTickEventFM_[ch] = isJam; + setFrontFMSequences(fm); + fm.hasPreSetTickEvent = isJam; if (!isTonePrtm) { - uint8_t chdata = getFMKeyOnOffChannelMask(ch); + uint8_t chdata = FM_KEYOFF_MASK[fm.inCh]; switch (mode_) { case SongType::Standard: { - if (isKeyOnFM_[ch]) opna_->setRegister(0x28, chdata); // Key off - else isKeyOnFM_[ch] = true; + if (fm.isKeyOn) opna_->setRegister(0x28, chdata); // Key off + else fm.isKeyOn = true; opna_->setRegister(0x28, static_cast(fmOpEnables_[ch] << 4) | chdata); break; } @@ -441,19 +550,19 @@ case 7: case 8: { - bool prev = isKeyOnFM_[ch]; - isKeyOnFM_[ch] = true; + bool prev = fm.isKeyOn; + fm.isKeyOn = true; slot = getFM3SlotValidStatus(); if (prev) { // Key off - uint8_t flags = static_cast(((slot & FM3_KEY_OFF_MASK_.at(ch)) << 4)) | chdata; + uint8_t flags = static_cast(((slot & FM3_KEY_OFF_MASK.at(ch)) << 4)) | chdata; opna_->setRegister(0x28, flags); } break; } default: slot = fmOpEnables_[ch]; - if (isKeyOnFM_[ch]) opna_->setRegister(0x28, chdata); // Key off - else isKeyOnFM_[ch] = true; + if (fm.isKeyOn) opna_->setRegister(0x28, chdata); // Key off + else fm.isKeyOn = true; break; } opna_->setRegister(0x28, static_cast(slot << 4) | chdata); @@ -462,28 +571,30 @@ } } - hasKeyOnBeforeFM_[ch] = true; + fm.hasKeyOnBefore = true; } void OPNAController::keyOnFM(int ch, int echoBuf) { - ToneDetail& td = baseToneFM_[ch].at(static_cast(echoBuf)); - if (td.octave == -1) return; - keyOnFM(ch, td.note, td.octave, td.pitch); + auto& fm = fm_[ch]; + if (static_cast(echoBuf) < fm.echoBuf.size()) + keyOnFM(ch, fm.echoBuf[echoBuf]); } void OPNAController::keyOffFM(int ch, bool isJam) { - if (!isKeyOnFM_[ch]) { - tickEventFM(ch); + auto& fm = fm_[ch]; + + if (!fm.isKeyOn) { + tickEventFM(fm); return; } - releaseStartFMSequences(ch); - hasPreSetTickEventFM_[ch] = isJam; + releaseStartFMSequences(fm); + fm.hasPreSetTickEvent = isJam; - isKeyOnFM_[ch] = false; + fm.isKeyOn = false; - uint8_t chdata = getFMKeyOnOffChannelMask(ch); + uint8_t chdata = FM_KEYOFF_MASK[fm.inCh]; switch (mode_) { case SongType::Standard: { @@ -492,7 +603,7 @@ } case SongType::FM3chExpanded: { - uint8_t slot = (toInternalFMChannel(ch) == 2) ? static_cast(getFM3SlotValidStatus() << 4) : 0; + uint8_t slot = (fm.inCh == 2) ? static_cast(getFM3SlotValidStatus() << 4) : 0; opna_->setRegister(0x28, slot | chdata); break; } @@ -501,58 +612,38 @@ // Change register only void OPNAController::resetFMChannelEnvelope(int ch) -{ +{ keyOffFM(ch); - hasResetEnvFM_[ch] = true; + auto& fm = fm_[ch]; + fm.hasResetEnv = true; - if (mode_ == SongType::FM3chExpanded && toInternalFMChannel(ch) == 2) { - FMEnvelopeParameter param; - switch (ch) { - case 2: param = FMEnvelopeParameter::RR1; break; - case 6: param = FMEnvelopeParameter::RR2; break; - case 7: param = FMEnvelopeParameter::RR3; break; - case 8: param = FMEnvelopeParameter::RR4; break; - default: throw std::out_of_range("out of range."); - } - int prev = envFM_[2]->getParameterValue(param); - writeFMEnveropeParameterToRegister(2, param, 127); - envFM_[2]->setParameterValue(param, prev); + if (mode_ == SongType::FM3chExpanded && fm.inCh == 2) { + FMEnvelopeParameter rr = PARAM_RR[FM3_CH_TO_OP_NUM.at(ch)]; + int prev = envFM_[2]->getParameterValue(rr); + writeFMEnveropeParameterToRegister(2, rr, 127); + envFM_[2]->setParameterValue(rr, prev); } else { - int prev = envFM_[ch]->getParameterValue(FMEnvelopeParameter::RR1); - writeFMEnveropeParameterToRegister(ch, FMEnvelopeParameter::RR1, 127); - envFM_[ch]->setParameterValue(FMEnvelopeParameter::RR1, prev); - - prev = envFM_[ch]->getParameterValue(FMEnvelopeParameter::RR2); - writeFMEnveropeParameterToRegister(ch, FMEnvelopeParameter::RR2, 127); - envFM_[ch]->setParameterValue(FMEnvelopeParameter::RR2, prev); - - prev = envFM_[ch]->getParameterValue(FMEnvelopeParameter::RR3); - writeFMEnveropeParameterToRegister(ch, FMEnvelopeParameter::RR3, 127); - envFM_[ch]->setParameterValue(FMEnvelopeParameter::RR3, prev); - - prev = envFM_[ch]->getParameterValue(FMEnvelopeParameter::RR4); - writeFMEnveropeParameterToRegister(ch, FMEnvelopeParameter::RR4, 127); - envFM_[ch]->setParameterValue(FMEnvelopeParameter::RR4, prev); + auto& env = envFM_[ch]; + for (const FMEnvelopeParameter& rr : PARAM_RR) { + int prev = env->getParameterValue(rr); + writeFMEnveropeParameterToRegister(ch, rr, 127); + env->setParameterValue(rr, prev); + } } } -void OPNAController::updateEchoBufferFM(int ch, int octave, Note note, int pitch) -{ - baseToneFM_[ch].pop_back(); - baseToneFM_[ch].push_front({ octave, note, pitch }); -} - /********** Set instrument **********/ /// NOTE: inst != nullptr void OPNAController::setInstrumentFM(int ch, std::shared_ptr inst) { - int inch = toInternalFMChannel(ch); - FMOperatorType opType = toChannelOperatorType(ch); - - if (!refInstFM_[inch] || !refInstFM_[inch]->isRegisteredWithManager() - || refInstFM_[inch]->getNumber() != inst->getNumber()) { - refInstFM_[inch] = inst; + auto& fm = fm_[ch]; + FMOperatorType opType = fm.opType; + size_t inch = fm.inCh; + + auto& refInst = refInstFM_[inch]; + if (!refInst || !refInst->isRegisteredWithManager() || refInst->getNumber() != inst->getNumber()) { + refInst = inst; writeFMEnvelopeToRegistersFromInstrument(inch); fmOpEnables_[inch] = static_cast(inst->getOperatorEnabled(0)) | static_cast(inst->getOperatorEnabled(1) << 1) @@ -569,37 +660,37 @@ if (isTLCtrlFM_[inch][op] || isBrightFM_[inch][op]) { isTLCtrlFM_[inch][op] = false; isBrightFM_[inch][op] = false; - FMEnvelopeParameter tl = PARAM_TL_[op]; + FMEnvelopeParameter tl = PARAM_TL[op]; writeFMEnveropeParameterToRegister(inch, tl, inst->getEnvelopeParameter(tl)); } if (isMLCtrlFM_[inch][op]) { isMLCtrlFM_[inch][op] = false; - FMEnvelopeParameter ml = PARAM_ML_[op]; + FMEnvelopeParameter ml = PARAM_ML[op]; writeFMEnveropeParameterToRegister(inch, ml, inst->getEnvelopeParameter(ml)); } if (isARCtrlFM_[inch][op]) { isARCtrlFM_[inch][op] = false; - FMEnvelopeParameter ar = PARAM_AR_[op]; + FMEnvelopeParameter ar = PARAM_AR[op]; writeFMEnveropeParameterToRegister(inch, ar, inst->getEnvelopeParameter(ar)); } if (isDRCtrlFM_[inch][op]) { isDRCtrlFM_[inch][op] = false; - FMEnvelopeParameter dr = PARAM_DR_[op]; + FMEnvelopeParameter dr = PARAM_DR[op]; writeFMEnveropeParameterToRegister(inch, dr, inst->getEnvelopeParameter(dr)); } if (isRRCtrlFM_[inch][op]) { isRRCtrlFM_[inch][op] = false; - FMEnvelopeParameter rr = PARAM_RR_[op]; + FMEnvelopeParameter rr = PARAM_RR[op]; writeFMEnveropeParameterToRegister(inch, rr, inst->getEnvelopeParameter(rr)); } } restoreFMEnvelopeFromReset(ch); } - if (isKeyOnFM_[ch] && lfoStartCntFM_[inch] == -1) writeFMLFOAllRegisters(inch); - for (auto& p : FM_ENV_PARAMS_OP_.at(opType)) { - if (refInstFM_[inch]->getOperatorSequenceEnabled(p)) { - opSeqItFM_[inch].at(p) = refInstFM_[inch]->getOperatorSequenceSequenceIterator(p); + if (fm.isKeyOn && lfoStartCntFM_[inch] == UNUSED_VALUE) writeFMLFOAllRegisters(inch); + for (auto& p : FM_ENV_PARAMS_OP.at(opType)) { + if (inst->getOperatorSequenceEnabled(p)) { + opSeqItFM_[inch].at(p) = inst->getOperatorSequenceSequenceIterator(p); switch (p) { case FMEnvelopeParameter::FB: isFBCtrlFM_[inch] = false; break; case FMEnvelopeParameter::TL1: @@ -641,43 +732,44 @@ opSeqItFM_[inch].at(p).reset(); } } - if (!isArpEffFM_[ch]) { - if (refInstFM_[inch]->getArpeggioEnabled(opType)) - arpItFM_[ch] = refInstFM_[inch]->getArpeggioSequenceIterator(opType); + if (!fm.isArpEff) { + if (inst->getArpeggioEnabled(opType)) + fm.arpItr = inst->getArpeggioSequenceIterator(opType); else - arpItFM_[ch].reset(); + fm.arpItr.reset(); } - if (refInstFM_[inch]->getPitchEnabled(opType)) - ptItFM_[ch] = refInstFM_[inch]->getPitchSequenceIterator(opType); + if (inst->getPitchEnabled(opType)) + fm.ptItr = inst->getPitchSequenceIterator(opType); else - ptItFM_[ch].reset(); - setInstrumentFMProperties(ch); + fm.ptItr.reset(); + setInstrumentFMProperties(fm); - checkLFOUsed(); + checkLFOUsedByInstrument(); } void OPNAController::updateInstrumentFM(int instNum) { - int cnt = static_cast(getFMChannelCount(mode_)); + int cnt = static_cast(Song::getFMChannelCount(mode_)); for (int ch = 0; ch < cnt; ++ch) { - int inch = toInternalFMChannel(ch); + auto& fm = fm_[ch]; + size_t inch = fm.inCh; - if (refInstFM_[inch] && refInstFM_[inch]->isRegisteredWithManager() - && refInstFM_[inch]->getNumber() == instNum) { + auto& inst = refInstFM_[inch]; + if (inst && inst->isRegisteredWithManager() && inst->getNumber() == instNum) { writeFMEnvelopeToRegistersFromInstrument(inch); - if (isKeyOnFM_[ch] && lfoStartCntFM_[inch] == -1) writeFMLFOAllRegisters(inch); - FMOperatorType opType = toChannelOperatorType(ch); - for (auto& p : FM_ENV_PARAMS_OP_.at(opType)) { - if (!refInstFM_[inch]->getOperatorSequenceEnabled(p)) + if (fm.isKeyOn && lfoStartCntFM_[inch] == UNUSED_VALUE) writeFMLFOAllRegisters(inch); + FMOperatorType opType = fm.opType; + for (auto& p : FM_ENV_PARAMS_OP.at(opType)) { + if (!inst->getOperatorSequenceEnabled(p)) opSeqItFM_[inch].at(p).reset(); } - if (!refInstFM_[inch]->getArpeggioEnabled(opType)) arpItFM_[ch].reset(); - if (!refInstFM_[inch]->getPitchEnabled(opType)) ptItFM_[ch].reset(); - setInstrumentFMProperties(ch); + if (!inst->getArpeggioEnabled(opType)) fm.arpItr.reset(); + if (!inst->getPitchEnabled(opType)) fm.ptItr.reset(); + setInstrumentFMProperties(fm); } } - checkLFOUsed(); + checkLFOUsedByInstrument(); } void OPNAController::updateInstrumentFMEnvelopeParameter(int envNum, FMEnvelopeParameter param) @@ -691,9 +783,10 @@ void OPNAController::setInstrumentFMOperatorEnabled(int envNum, int opNum) { - int chsize = static_cast(getFMChannelCount(mode_)); + int chsize = static_cast(Song::getFMChannelCount(mode_)); for (int ch = 0; ch < chsize; ++ch) { - int inch = toInternalFMChannel(ch); + auto& fm = fm_[ch]; + size_t inch = fm.inCh; if (refInstFM_[inch] && refInstFM_[inch]->getEnvelopeNumber() == envNum) { bool enabled = refInstFM_[inch]->getOperatorEnabled(opNum); envFM_[inch]->setOperatorEnabled(opNum, enabled); @@ -703,8 +796,8 @@ else { fmOpEnables_[inch] &= ~(1 << opNum); } - if (isKeyOnFM_[ch]) { - uint8_t chdata = getFMKeyOnOffChannelMask(ch); + if (fm.isKeyOn) { + uint8_t chdata = FM_KEYOFF_MASK[inch]; switch (mode_) { case SongType::Standard: { @@ -735,32 +828,28 @@ /********** Set volume **********/ void OPNAController::setVolumeFM(int ch, int volume) { - baseVolFM_[ch] = volume; - tmpVolFM_[ch] = -1; + auto& fm = fm_[ch]; + fm.baseVol = volume; + fm.oneshotVol = UNUSED_VALUE; - if (refInstFM_[toInternalFMChannel(ch)]) updateFMVolume(ch); // Change TL + if (refInstFM_[fm.inCh]) updateFMVolume(fm); // Change TL } -void OPNAController::setTemporaryVolumeFM(int ch, int volume) +void OPNAController::setOneshotVolumeFM(int ch, int volume) { - tmpVolFM_[ch] = volume; + auto& fm = fm_[ch]; + fm.oneshotVol = volume; - if (refInstFM_[toInternalFMChannel(ch)]) updateFMVolume(ch); // Change TL + if (refInstFM_[fm.inCh]) updateFMVolume(fm); // Change TL } -void OPNAController::updateFMVolume(int ch) +void OPNAController::updateFMVolume(FMChannel& fm) { - int inch = toInternalFMChannel(ch); - switch (toChannelOperatorType(ch)) { + size_t inch = fm.inCh; + switch (fm.opType) { case FMOperatorType::All: - writeFMEnveropeParameterToRegister(inch, FMEnvelopeParameter::TL1, - refInstFM_[inch]->getEnvelopeParameter(FMEnvelopeParameter::TL1)); - writeFMEnveropeParameterToRegister(inch, FMEnvelopeParameter::TL2, - refInstFM_[inch]->getEnvelopeParameter(FMEnvelopeParameter::TL2)); - writeFMEnveropeParameterToRegister(inch, FMEnvelopeParameter::TL3, - refInstFM_[inch]->getEnvelopeParameter(FMEnvelopeParameter::TL3)); - writeFMEnveropeParameterToRegister(inch, FMEnvelopeParameter::TL4, - refInstFM_[inch]->getEnvelopeParameter(FMEnvelopeParameter::TL4)); + for (const FMEnvelopeParameter& tl : PARAM_TL) + writeFMEnveropeParameterToRegister(inch, tl, refInstFM_[inch]->getEnvelopeParameter(tl)); break; case FMOperatorType::Op1: writeFMEnveropeParameterToRegister(inch, FMEnvelopeParameter::TL1, @@ -789,90 +878,99 @@ /********** Set effect **********/ void OPNAController::setPanFM(int ch, int value) { - int inch = toInternalFMChannel(ch); + size_t inch = fm_[ch].inCh; panFM_[inch] = static_cast(value); uint32_t bch = getFMChannelOffset(ch); // Bank and channel offset uint8_t data = static_cast(value << 6); - if (refInstFM_[inch] && refInstFM_[inch]->getLFOEnabled()) { - data |= (refInstFM_[inch]->getLFOParameter(FMLFOParameter::AMS) << 4); - data |= refInstFM_[inch]->getLFOParameter(FMLFOParameter::PMS); + auto& inst = refInstFM_[inch]; + if (inst && inst->getLFOEnabled()) { + data |= (inst->getLFOParameter(FMLFOParameter::AMS) << 4); + data |= inst->getLFOParameter(FMLFOParameter::PMS); } opna_->setRegister(0xb4 + bch, data); } void OPNAController::setArpeggioEffectFM(int ch, int second, int third) { + auto& fm = fm_[ch]; if (second || third) { - arpItFM_[ch] = std::make_unique(second, third); - isArpEffFM_[ch] = true; + fm.arpItr = std::make_unique(second, third); + fm.isArpEff = true; } else { - int inch = toInternalFMChannel(ch); + size_t inch = fm.inCh; if (refInstFM_[inch]) { - FMOperatorType op = toChannelOperatorType(ch); - if (!refInstFM_[inch]->getArpeggioEnabled(op)) arpItFM_[ch].reset(); - else arpItFM_[ch] = refInstFM_[inch]->getArpeggioSequenceIterator(op); + FMOperatorType op = fm.opType; + if (!refInstFM_[inch]->getArpeggioEnabled(op)) fm.arpItr.reset(); + else fm.arpItr = refInstFM_[inch]->getArpeggioSequenceIterator(op); } - isArpEffFM_[ch] = false; + fm.isArpEff = false; } } void OPNAController::setPortamentoEffectFM(int ch, int depth, bool isTonePortamento) { - prtmFM_[ch] = depth; - isTonePrtmFM_[ch] = depth ? isTonePortamento : false; + auto& fm = fm_[ch]; + fm.prtmDepth = depth; + fm.isTonePrtm = depth ? isTonePortamento : false; } void OPNAController::setVibratoEffectFM(int ch, int period, int depth) { - if (period && depth) vibItFM_[ch] = std::make_unique(period, depth); - else vibItFM_[ch].reset(); + auto& fm = fm_[ch]; + if (period && depth) fm.vibItr = std::make_unique(period, depth); + else fm.vibItr.reset(); } void OPNAController::setTremoloEffectFM(int ch, int period, int depth) { - if (period && depth) treItFM_[ch] = std::make_unique(period, depth); - else treItFM_[ch].reset(); + auto& fm = fm_[ch]; + if (period && depth) fm.treItr = std::make_unique(period, depth); + else fm.treItr.reset(); } void OPNAController::setVolumeSlideFM(int ch, int depth, bool isUp) { - volSldFM_[ch] = isUp ? -depth : depth; + fm_[ch].volSld = isUp ? -depth : depth; } void OPNAController::setDetuneFM(int ch, int pitch) { - detuneFM_[ch] = pitch; - needToneSetFM_[ch] = true; + auto& fm = fm_[ch]; + fm.detune = pitch; + fm.shouldSetTone = true; } void OPNAController::setFineDetuneFM(int ch, int pitch) { - fdetuneFM_[ch] = pitch; - needToneSetFM_[ch] = true; + auto& fm = fm_[ch]; + fm.fdetune = pitch; + fm.shouldSetTone = true; } void OPNAController::setNoteSlideFM(int ch, int speed, int seminote) { + auto& fm = fm_[ch]; if (seminote) { - nsItFM_[ch] = std::make_unique(speed, seminote); - noteSldFMSetFlag_[ch] = true; + fm.nsItr = std::make_unique(speed, seminote); + fm.hasSetNs = true; } - else nsItFM_[ch].reset(); + else fm.nsItr.reset(); } void OPNAController::setTransposeEffectFM(int ch, int seminote) { - transposeFM_[ch] += (seminote * calc_pitch::SEMINOTE_PITCH); - needToneSetFM_[ch] = true; + auto& fm = fm_[ch]; + fm.transpose += (seminote * Note::SEMINOTE_PITCH); + fm.shouldSetTone = true; } void OPNAController::setFBControlFM(int ch, int value) { - int inch = toInternalFMChannel(ch); + size_t inch = fm_[ch].inCh; writeFMEnveropeParameterToRegister(inch, FMEnvelopeParameter::FB, value); isFBCtrlFM_[inch] = true; opSeqItFM_[inch].at(FMEnvelopeParameter::FB).reset(); @@ -880,8 +978,8 @@ void OPNAController::setTLControlFM(int ch, int op, int value) { - int inch = toInternalFMChannel(ch); - FMEnvelopeParameter param = PARAM_TL_[op]; + size_t inch = fm_[ch].inCh; + FMEnvelopeParameter param = PARAM_TL[op]; writeFMEnveropeParameterToRegister(inch, param, value); isTLCtrlFM_[inch][op] = true; opSeqItFM_[inch].at(param).reset(); @@ -889,8 +987,8 @@ void OPNAController::setMLControlFM(int ch, int op, int value) { - int inch = toInternalFMChannel(ch); - FMEnvelopeParameter param = PARAM_ML_[op]; + size_t inch = fm_[ch].inCh; + FMEnvelopeParameter param = PARAM_ML[op]; writeFMEnveropeParameterToRegister(inch, param, value); isMLCtrlFM_[inch][op] = true; opSeqItFM_[inch].at(param).reset(); @@ -898,8 +996,8 @@ void OPNAController::setARControlFM(int ch, int op, int value) { - int inch = toInternalFMChannel(ch); - FMEnvelopeParameter param = PARAM_AR_[op]; + size_t inch = fm_[ch].inCh; + FMEnvelopeParameter param = PARAM_AR[op]; writeFMEnveropeParameterToRegister(inch, param, value); isARCtrlFM_[inch][op] = true; opSeqItFM_[inch].at(param).reset(); @@ -907,8 +1005,8 @@ void OPNAController::setDRControlFM(int ch, int op, int value) { - int inch = toInternalFMChannel(ch); - FMEnvelopeParameter param = PARAM_DR_[op]; + size_t inch = fm_[ch].inCh; + FMEnvelopeParameter param = PARAM_DR[op]; writeFMEnveropeParameterToRegister(inch, param, value); isDRCtrlFM_[inch][op] = true; opSeqItFM_[inch].at(param).reset(); @@ -916,8 +1014,8 @@ void OPNAController::setRRControlFM(int ch, int op, int value) { - int inch = toInternalFMChannel(ch); - FMEnvelopeParameter param = PARAM_RR_[op]; + size_t inch = fm_[ch].inCh; + FMEnvelopeParameter param = PARAM_RR[op]; writeFMEnveropeParameterToRegister(inch, param, value); isRRCtrlFM_[inch][op] = true; opSeqItFM_[inch].at(param).reset(); @@ -925,11 +1023,11 @@ void OPNAController::setBrightnessFM(int ch, int value) { - int inch = toInternalFMChannel(ch); + size_t inch = fm_[ch].inCh; std::vector ops = getOperatorsInLevel(1, envFM_[inch]->getParameterValue(FMEnvelopeParameter::AL)); for (auto& op : ops) { - FMEnvelopeParameter param = PARAM_TL_[op]; - int v = clamp(envFM_[inch]->getParameterValue(param) + value, 0, 127); + FMEnvelopeParameter param = PARAM_TL[op]; + int v = utils::clamp(envFM_[inch]->getParameterValue(param) + value, 0, 127); writeFMEnveropeParameterToRegister(inch, param, v); isBrightFM_[inch][op] = true; opSeqItFM_[inch].at(param).reset(); @@ -939,42 +1037,43 @@ /********** For state retrieve **********/ void OPNAController::haltSequencesFM(int ch) { - int inch = toInternalFMChannel(ch); - for (auto& p : FM_ENV_PARAMS_OP_.at(toChannelOperatorType(ch))) { - if (auto& it = opSeqItFM_[inch].at(p)) it->end(); - } - if (treItFM_[ch]) treItFM_[ch]->end(); - if (arpItFM_[ch]) arpItFM_[ch]->end(); - if (ptItFM_[ch]) ptItFM_[ch]->end(); - if (vibItFM_[ch]) vibItFM_[ch]->end(); - if (nsItFM_[ch]) nsItFM_[ch]->end(); + auto& fm = fm_[ch]; + for (auto& p : FM_ENV_PARAMS_OP.at(fm.opType)) { + if (auto& itr = opSeqItFM_[fm.inCh].at(p)) itr->end(); + } + if (auto& treItr = fm.treItr) treItr->end(); + if (auto& arpItr = fm.arpItr) arpItr->end(); + if (auto& ptItr = fm.ptItr) ptItr->end(); + if (auto& vibItr = fm.vibItr) vibItr->end(); + if (auto& nsItr = fm.nsItr) nsItr->end(); } /********** Chip details **********/ bool OPNAController::isKeyOnFM(int ch) const { - return isKeyOnFM_[ch]; + return fm_[ch].isKeyOn; } bool OPNAController::isTonePortamentoFM(int ch) const { - return isTonePrtmFM_[ch]; + return fm_[ch].isTonePrtm; } bool OPNAController::enableFMEnvelopeReset(int ch) const { - return envFM_[toInternalFMChannel(ch)] ? enableEnvResetFM_[ch] : true; + auto& fm = fm_[ch]; + return envFM_[fm.ch] ? fm.isEnabledEnvReset : true; } -ToneDetail OPNAController::getFMTone(int ch) const +Note OPNAController::getFMLatestNote(int ch) const { - return baseToneFM_[ch].front(); + return fm_[ch].echoBuf.latest(); } /***********************************/ void OPNAController::initFM() { - lfoFreq_ = -1; + lfoFreq_ = UNUSED_VALUE; uint8_t mode = 0; switch (mode_) { @@ -998,7 +1097,7 @@ p.second.reset(); } - lfoStartCntFM_[inch] = -1; + lfoStartCntFM_[inch] = UNUSED_VALUE; isFBCtrlFM_[inch] = false; for (int op = 0; op < 4; ++op) { @@ -1011,69 +1110,77 @@ } } - size_t fmch = getFMChannelCount(mode_); + size_t fmch = Song::getFMChannelCount(mode_); for (size_t ch = 0; ch < fmch; ++ch) { + auto& fm = fm_[ch]; + fm.ch = ch; + if (ch < 6) fm.inCh = ch; + else if (mode_ == SongType::FM3chExpanded && 6 <= ch && ch < 9) fm.inCh = 2; + // Init operators key off - isKeyOnFM_[ch] = false; - hasKeyOnBeforeFM_[ch] = false; + fm.isKeyOn = false; + fm.hasKeyOnBefore = false; - // Init echo buffer - baseToneFM_[ch] = std::deque(4); - for (auto& td : baseToneFM_[ch]) { - td.octave = -1; - } - - keyToneFM_[ch].note = Note::C; // Dummy - keyToneFM_[ch].octave = -1; - keyToneFM_[ch].pitch = 0; // Dummy - sumPitchFM_[ch] = 0; - baseVolFM_[ch] = 0; // Init volume - tmpVolFM_[ch] = -1; - enableEnvResetFM_[ch] = false; - hasResetEnvFM_[ch] = false; + fm.echoBuf.clear(); + + fm.neverSetBaseNote = true; + fm.baseVol = 0; // Init volume + fm.oneshotVol = UNUSED_VALUE; + fm.isEnabledEnvReset = false; + fm.hasResetEnv = false; + + // Set operator type + if (mode_ == SongType::FM3chExpanded && fm.inCh == 2) { + switch (fm.ch) { + case 2: fm.opType = FMOperatorType::Op1; break; + case 6: fm.opType = FMOperatorType::Op2; break; + case 7: fm.opType = FMOperatorType::Op3; break; + case 8: fm.opType = FMOperatorType::Op4; break; + default: throw std::out_of_range("out of range."); + } + } + else { + fm.opType = FMOperatorType::All; + } // Init sequence - hasPreSetTickEventFM_[ch] = false; - arpItFM_[ch].reset(); - ptItFM_[ch].reset(); - needToneSetFM_[ch] = false; + fm.hasPreSetTickEvent = false; + fm.arpItr.reset(); + fm.ptItr.reset(); + fm.ptSum = 0; + fm.shouldSetTone = false; // Effect - isArpEffFM_[ch] = false; - prtmFM_[ch] = 0; - isTonePrtmFM_[ch] = false; - vibItFM_[ch].reset(); - treItFM_[ch].reset(); - volSldFM_[ch] = 0; - sumVolSldFM_[ch] = 0; - detuneFM_[ch] = 0; - fdetuneFM_[ch] = 0; - nsItFM_[ch].reset(); - sumNoteSldFM_[ch] = 0; - noteSldFMSetFlag_[ch] = false; - transposeFM_[ch] = 0; + fm.isArpEff = false; + fm.prtmDepth = 0; + fm.isTonePrtm = false; + fm.vibItr.reset(); + fm.treItr.reset(); + fm.volSld = 0; + fm.volSldSum = 0; + fm.detune = 0; + fm.fdetune = 0; + fm.nsItr.reset(); + fm.nsSum = 0; + fm.hasSetNs = false; + fm.transpose = 0; } } void OPNAController::setMuteFMState(int ch, bool isMute) { - isMuteFM_[ch] = isMute; + auto& fm = fm_[ch]; + fm.isMute = isMute; if (isMute) { resetFMChannelEnvelope(ch); } else { - int inch = toInternalFMChannel(ch); - switch (toChannelOperatorType(ch)) { + size_t inch = fm.inCh; + switch (fm.opType) { case FMOperatorType::All: - writeFMEnveropeParameterToRegister(inch, FMEnvelopeParameter::RR1, - envFM_[inch]->getParameterValue(FMEnvelopeParameter::RR1)); - writeFMEnveropeParameterToRegister(inch, FMEnvelopeParameter::RR2, - envFM_[inch]->getParameterValue(FMEnvelopeParameter::RR2)); - writeFMEnveropeParameterToRegister(inch, FMEnvelopeParameter::RR3, - envFM_[inch]->getParameterValue(FMEnvelopeParameter::RR3)); - writeFMEnveropeParameterToRegister(inch, FMEnvelopeParameter::RR4, - envFM_[inch]->getParameterValue(FMEnvelopeParameter::RR4)); + for (const FMEnvelopeParameter& rr : PARAM_RR) + writeFMEnveropeParameterToRegister(inch, rr, envFM_[inch]->getParameterValue(rr)); break; case FMOperatorType::Op1: writeFMEnveropeParameterToRegister(inch, FMEnvelopeParameter::RR1, @@ -1097,7 +1204,7 @@ bool OPNAController::isMuteFM(int ch) { - return isMuteFM_[ch]; + return fm_[ch].isMute; } uint32_t OPNAController::getFMChannelOffset(int ch, bool forPitch) const @@ -1124,7 +1231,7 @@ } } else { - switch (toInternalFMChannel(ch)) { + switch (fm_[ch].inCh) { case 0: case 1: case 2: @@ -1139,273 +1246,124 @@ } } -FMOperatorType OPNAController::toChannelOperatorType(int ch) const -{ - FMOperatorType opType; - if (mode_ == SongType::FM3chExpanded && toInternalFMChannel(ch) == 2) { - switch (ch) { - case 2: opType = FMOperatorType::Op1; break; - case 6: opType = FMOperatorType::Op2; break; - case 7: opType = FMOperatorType::Op3; break; - case 8: opType = FMOperatorType::Op4; break; - default: throw std::out_of_range("out of range."); - } - } - else { - opType = FMOperatorType::All; - } - return opType; -} - -void OPNAController::writeFMEnvelopeToRegistersFromInstrument(int inch) +void OPNAController::writeFMEnvelopeToRegistersFromInstrument(size_t inch) { uint32_t bch = getFMChannelOffset(inch); // Bank and channel offset uint8_t data1, data2; int al; + auto& inst = refInstFM_[inch]; + auto& env = envFM_[inch]; - data1 = static_cast(refInstFM_[inch]->getEnvelopeParameter(FMEnvelopeParameter::FB)); - envFM_[inch]->setParameterValue(FMEnvelopeParameter::FB, data1); + data1 = static_cast(inst->getEnvelopeParameter(FMEnvelopeParameter::FB)); + env->setParameterValue(FMEnvelopeParameter::FB, data1); data1 <<= 3; - al = refInstFM_[inch]->getEnvelopeParameter(FMEnvelopeParameter::AL); - envFM_[inch]->setParameterValue(FMEnvelopeParameter::AL, al); + al = inst->getEnvelopeParameter(FMEnvelopeParameter::AL); + env->setParameterValue(FMEnvelopeParameter::AL, al); data1 += al; opna_->setRegister(0xb0 + bch, data1); - uint32_t offset = bch; // Operator 1 + bool isExpandedCh = (mode_ == SongType::FM3chExpanded && inch == 2); + for (size_t op = 0; op < 4; ++op) { + uint32_t offset = bch + OP_OFFSET[op]; + + FMEnvelopeParameter dt = PARAM_DT[op]; + FMEnvelopeParameter ml = PARAM_ML[op]; + data1 = static_cast(inst->getEnvelopeParameter(dt)); + env->setParameterValue(dt, data1); + data1 <<= 4; + data2 = static_cast(inst->getEnvelopeParameter(ml)); + env->setParameterValue(ml, data2); + data1 |= data2; + opna_->setRegister(0x30 + offset, data1); - data1 = static_cast(refInstFM_[inch]->getEnvelopeParameter(FMEnvelopeParameter::DT1)); - envFM_[inch]->setParameterValue(FMEnvelopeParameter::DT1, data1); - data1 <<= 4; - data2 = static_cast(refInstFM_[inch]->getEnvelopeParameter(FMEnvelopeParameter::ML1)); - envFM_[inch]->setParameterValue(FMEnvelopeParameter::ML1, data2); - data1 |= data2; - opna_->setRegister(0x30 + offset, data1); - - data1 = static_cast(refInstFM_[inch]->getEnvelopeParameter(FMEnvelopeParameter::TL1)); - // Adjust volume - if (mode_ == SongType::FM3chExpanded && inch == 2) data1 = calculateTL(2, data1); - else if (isCarrier(0, al)) data1 = calculateTL(inch, data1); - envFM_[inch]->setParameterValue(FMEnvelopeParameter::TL1, data1); - opna_->setRegister(0x40 + offset, data1); - - data1 = static_cast(refInstFM_[inch]->getEnvelopeParameter(FMEnvelopeParameter::KS1)); - envFM_[inch]->setParameterValue(FMEnvelopeParameter::KS1, data1); - data1 <<= 6; - data2 = static_cast(refInstFM_[inch]->getEnvelopeParameter(FMEnvelopeParameter::AR1)); - envFM_[inch]->setParameterValue(FMEnvelopeParameter::AR1, data2); - data1 |= data2; - opna_->setRegister(0x50 + offset, data1); - - data1 = refInstFM_[inch]->getLFOEnabled() ? static_cast(refInstFM_[inch]->getLFOParameter(FMLFOParameter::AM1)) : 0; - data1 <<= 7; - data2 = static_cast(refInstFM_[inch]->getEnvelopeParameter(FMEnvelopeParameter::DR1)); - envFM_[inch]->setParameterValue(FMEnvelopeParameter::DR1, data2); - data1 |= data2; - opna_->setRegister(0x60 + offset, data1); - - data1 = static_cast(refInstFM_[inch]->getEnvelopeParameter(FMEnvelopeParameter::SR1)); - envFM_[inch]->setParameterValue(FMEnvelopeParameter::SR1, data2); - opna_->setRegister(0x70 + offset, data1); - - data1 = static_cast(refInstFM_[inch]->getEnvelopeParameter(FMEnvelopeParameter::SL1)); - envFM_[inch]->setParameterValue(FMEnvelopeParameter::SL1, data1); - data1 <<= 4; - data2 = static_cast(refInstFM_[inch]->getEnvelopeParameter(FMEnvelopeParameter::RR1)); - envFM_[inch]->setParameterValue(FMEnvelopeParameter::RR1, data2); - data1 |= data2; - opna_->setRegister(0x80 + offset, data1); - - int tmp = refInstFM_[inch]->getEnvelopeParameter(FMEnvelopeParameter::SSGEG1); - envFM_[inch]->setParameterValue(FMEnvelopeParameter::SSGEG1, tmp); - data1 = judgeSSEGRegisterValue(tmp); - opna_->setRegister(0x90 + offset, data1); - - offset = bch + 8; // Operator 2 - - data1 = static_cast(refInstFM_[inch]->getEnvelopeParameter(FMEnvelopeParameter::DT2)); - envFM_[inch]->setParameterValue(FMEnvelopeParameter::DT2, data1); - data1 <<= 4; - data2 = static_cast(refInstFM_[inch]->getEnvelopeParameter(FMEnvelopeParameter::ML2)); - envFM_[inch]->setParameterValue(FMEnvelopeParameter::ML2, data2); - data1 |= data2; - opna_->setRegister(0x30 + offset, data1); - - data1 = static_cast(refInstFM_[inch]->getEnvelopeParameter(FMEnvelopeParameter::TL2)); - // Adjust volume - if (mode_ == SongType::FM3chExpanded && inch == 2) data1 = calculateTL(6, data1); - else if (isCarrier(1, al)) data1 = calculateTL(inch, data1); - envFM_[inch]->setParameterValue(FMEnvelopeParameter::TL2, data1); - opna_->setRegister(0x40 + offset, data1); - - data1 = static_cast(refInstFM_[inch]->getEnvelopeParameter(FMEnvelopeParameter::KS2)); - envFM_[inch]->setParameterValue(FMEnvelopeParameter::KS2, data1); - data1 <<= 6; - data2 = static_cast(refInstFM_[inch]->getEnvelopeParameter(FMEnvelopeParameter::AR2)); - envFM_[inch]->setParameterValue(FMEnvelopeParameter::AR2, data2); - data1 |= data2; - opna_->setRegister(0x50 + offset, data1); - - data1 = refInstFM_[inch]->getLFOEnabled() ? static_cast(refInstFM_[inch]->getLFOParameter(FMLFOParameter::AM2)) : 0; - data1 <<= 7; - data2 = static_cast(refInstFM_[inch]->getEnvelopeParameter(FMEnvelopeParameter::DR2)); - envFM_[inch]->setParameterValue(FMEnvelopeParameter::DR2, data2); - data1 |= data2; - opna_->setRegister(0x60 + offset, data1); - - data1 = static_cast(refInstFM_[inch]->getEnvelopeParameter(FMEnvelopeParameter::SR2)); - envFM_[inch]->setParameterValue(FMEnvelopeParameter::SR2, data2); - opna_->setRegister(0x70 + offset, data1); - - data1 = static_cast(refInstFM_[inch]->getEnvelopeParameter(FMEnvelopeParameter::SL2)); - envFM_[inch]->setParameterValue(FMEnvelopeParameter::SL2, data1); - data1 <<= 4; - data2 = static_cast(refInstFM_[inch]->getEnvelopeParameter(FMEnvelopeParameter::RR2)); - envFM_[inch]->setParameterValue(FMEnvelopeParameter::RR2, data2); - data1 |= data2; - opna_->setRegister(0x80 + offset, data1); - - tmp = refInstFM_[inch]->getEnvelopeParameter(FMEnvelopeParameter::SSGEG2); - envFM_[inch]->setParameterValue(FMEnvelopeParameter::SSGEG2, tmp); - data1 = judgeSSEGRegisterValue(tmp); - opna_->setRegister(0x90 + offset, data1); - - offset = bch + 4; // Operator 3 - - data1 = static_cast(refInstFM_[inch]->getEnvelopeParameter(FMEnvelopeParameter::DT3)); - envFM_[inch]->setParameterValue(FMEnvelopeParameter::DT3, data1); - data1 <<= 4; - data2 = static_cast(refInstFM_[inch]->getEnvelopeParameter(FMEnvelopeParameter::ML3)); - envFM_[inch]->setParameterValue(FMEnvelopeParameter::ML3, data2); - data1 |= data2; - opna_->setRegister(0x30 + offset, data1); - - data1 = static_cast(refInstFM_[inch]->getEnvelopeParameter(FMEnvelopeParameter::TL3)); - // Adjust volume - if (mode_ == SongType::FM3chExpanded && inch == 2) data1 = calculateTL(7, data1); - else if (isCarrier(2, al)) data1 = calculateTL(inch, data1); - envFM_[inch]->setParameterValue(FMEnvelopeParameter::TL3, data1); - opna_->setRegister(0x40 + offset, data1); - - data1 = static_cast(refInstFM_[inch]->getEnvelopeParameter(FMEnvelopeParameter::KS3)); - envFM_[inch]->setParameterValue(FMEnvelopeParameter::KS3, data1); - data1 <<= 6; - data2 = static_cast(refInstFM_[inch]->getEnvelopeParameter(FMEnvelopeParameter::AR3)); - envFM_[inch]->setParameterValue(FMEnvelopeParameter::AR3, data2); - data1 |= data2; - opna_->setRegister(0x50 + offset, data1); - - data1 = refInstFM_[inch]->getLFOEnabled() ? static_cast(refInstFM_[inch]->getLFOParameter(FMLFOParameter::AM3)) : 0; - data1 <<= 7; - data2 = static_cast(refInstFM_[inch]->getEnvelopeParameter(FMEnvelopeParameter::DR3)); - envFM_[inch]->setParameterValue(FMEnvelopeParameter::DR3, data2); - data1 |= data2; - opna_->setRegister(0x60 + offset, data1); - - data1 = static_cast(refInstFM_[inch]->getEnvelopeParameter(FMEnvelopeParameter::SR3)); - envFM_[inch]->setParameterValue(FMEnvelopeParameter::SR3, data2); - opna_->setRegister(0x70 + offset, data1); - - data1 = static_cast(refInstFM_[inch]->getEnvelopeParameter(FMEnvelopeParameter::SL3)); - envFM_[inch]->setParameterValue(FMEnvelopeParameter::SL3, data1); - data1 <<= 4; - data2 = static_cast(refInstFM_[inch]->getEnvelopeParameter(FMEnvelopeParameter::RR3)); - envFM_[inch]->setParameterValue(FMEnvelopeParameter::RR3, data2); - data1 |= data2; - opna_->setRegister(0x80 + offset, data1); - - tmp = refInstFM_[inch]->getEnvelopeParameter(FMEnvelopeParameter::SSGEG3); - envFM_[inch]->setParameterValue(FMEnvelopeParameter::SSGEG3, tmp); - data1 = judgeSSEGRegisterValue(tmp); - opna_->setRegister(0x90 + offset, data1); - - offset = bch + 12; // Operator 4 - - data1 = static_cast(refInstFM_[inch]->getEnvelopeParameter(FMEnvelopeParameter::DT4)); - envFM_[inch]->setParameterValue(FMEnvelopeParameter::DT4, data1); - data1 <<= 4; - data2 = static_cast(refInstFM_[inch]->getEnvelopeParameter(FMEnvelopeParameter::ML4)); - envFM_[inch]->setParameterValue(FMEnvelopeParameter::ML4, data2); - data1 |= data2; - opna_->setRegister(0x30 + offset, data1); - - data1 = static_cast(refInstFM_[inch]->getEnvelopeParameter(FMEnvelopeParameter::TL4)); - // Adjust volume - if (mode_ == SongType::FM3chExpanded && inch == 2) data1 = calculateTL(8, data1); - else data1 = calculateTL(inch, data1); - envFM_[inch]->setParameterValue(FMEnvelopeParameter::TL4, data1); - opna_->setRegister(0x40 + offset, data1); - - data1 = static_cast(refInstFM_[inch]->getEnvelopeParameter(FMEnvelopeParameter::KS4)); - envFM_[inch]->setParameterValue(FMEnvelopeParameter::KS4, data1); - data1 <<= 6; - data2 = static_cast(refInstFM_[inch]->getEnvelopeParameter(FMEnvelopeParameter::AR4)); - envFM_[inch]->setParameterValue(FMEnvelopeParameter::AR4, data2); - data1 |= data2; - opna_->setRegister(0x50 + offset, data1); - - data1 = refInstFM_[inch]->getLFOEnabled() ? static_cast(refInstFM_[inch]->getLFOParameter(FMLFOParameter::AM4)) : 0; - data1 <<= 7; - data2 = static_cast(refInstFM_[inch]->getEnvelopeParameter(FMEnvelopeParameter::DR4)); - envFM_[inch]->setParameterValue(FMEnvelopeParameter::DR4, data2); - data1 |= data2; - opna_->setRegister(0x60 + offset, data1); - - data1 = static_cast(refInstFM_[inch]->getEnvelopeParameter(FMEnvelopeParameter::SR4)); - envFM_[inch]->setParameterValue(FMEnvelopeParameter::SR4, data2); - opna_->setRegister(0x70 + offset, data1); - - data1 = static_cast(refInstFM_[inch]->getEnvelopeParameter(FMEnvelopeParameter::SL4)); - envFM_[inch]->setParameterValue(FMEnvelopeParameter::SL4, data1); - data1 <<= 4; - data2 = static_cast(refInstFM_[inch]->getEnvelopeParameter(FMEnvelopeParameter::RR4)); - envFM_[inch]->setParameterValue(FMEnvelopeParameter::RR4, data2); - data1 |= data2; - opna_->setRegister(0x80 + offset, data1); - - tmp = refInstFM_[inch]->getEnvelopeParameter(FMEnvelopeParameter::SSGEG4); - envFM_[inch]->setParameterValue(FMEnvelopeParameter::SSGEG4, tmp); - data1 = judgeSSEGRegisterValue(tmp); - opna_->setRegister(0x90 + offset, data1); + FMEnvelopeParameter tl = PARAM_TL[op]; + data1 = static_cast(inst->getEnvelopeParameter(tl)); + // Adjust volume + if (isExpandedCh) data1 = calculateTL(FM3_OP_NUM_TO_CH[op], data1); + else if (IS_CARRIER[op][al]) data1 = calculateTL(inch, data1); + env->setParameterValue(tl, data1); + opna_->setRegister(0x40 + offset, data1); + + FMEnvelopeParameter ks = PARAM_KS[op]; + FMEnvelopeParameter ar = PARAM_AR[op]; + data1 = static_cast(inst->getEnvelopeParameter(ks)); + env->setParameterValue(ks, data1); + data1 <<= 6; + data2 = static_cast(inst->getEnvelopeParameter(ar)); + env->setParameterValue(ar, data2); + data1 |= data2; + opna_->setRegister(0x50 + offset, data1); + + FMEnvelopeParameter dr = PARAM_DR[op]; + data1 = inst->getLFOEnabled() ? static_cast(inst->getLFOParameter(PARAM_AM[op])) : 0; + data1 <<= 7; + data2 = static_cast(inst->getEnvelopeParameter(dr)); + env->setParameterValue(dr, data2); + data1 |= data2; + opna_->setRegister(0x60 + offset, data1); + + FMEnvelopeParameter sr = PARAM_SR[op]; + data1 = static_cast(inst->getEnvelopeParameter(sr)); + env->setParameterValue(sr, data2); + opna_->setRegister(0x70 + offset, data1); + + FMEnvelopeParameter sl = PARAM_SL[op]; + FMEnvelopeParameter rr = PARAM_RR[op]; + data1 = static_cast(inst->getEnvelopeParameter(sl)); + env->setParameterValue(sl, data1); + data1 <<= 4; + data2 = static_cast(inst->getEnvelopeParameter(rr)); + env->setParameterValue(rr, data2); + data1 |= data2; + opna_->setRegister(0x80 + offset, data1); + + FMEnvelopeParameter ssgeg = PARAM_SSGEG[op]; + int tmp = inst->getEnvelopeParameter(ssgeg); + env->setParameterValue(ssgeg, tmp); + data1 = judgeSSGEGRegisterValue(tmp); + opna_->setRegister(0x90 + offset, data1); + } } -void OPNAController::writeFMEnveropeParameterToRegister(int inch, FMEnvelopeParameter param, int value) +void OPNAController::writeFMEnveropeParameterToRegister(size_t inch, FMEnvelopeParameter param, int value) { uint32_t bch = getFMChannelOffset(inch); // Bank and channel offset uint8_t data; int tmp; + auto& env = envFM_[inch]; - envFM_[inch]->setParameterValue(param, value); + env->setParameterValue(param, value); switch (param) { case FMEnvelopeParameter::AL: case FMEnvelopeParameter::FB: - data = static_cast(envFM_[inch]->getParameterValue(FMEnvelopeParameter::FB) << 3); - data += envFM_[inch]->getParameterValue(FMEnvelopeParameter::AL); + data = static_cast(env->getParameterValue(FMEnvelopeParameter::FB) << 3); + data += env->getParameterValue(FMEnvelopeParameter::AL); opna_->setRegister(0xb0 + bch, data); break; case FMEnvelopeParameter::DT1: case FMEnvelopeParameter::ML1: - data = static_cast(envFM_[inch]->getParameterValue(FMEnvelopeParameter::DT1) << 4); - data |= envFM_[inch]->getParameterValue(FMEnvelopeParameter::ML1); + data = static_cast(env->getParameterValue(FMEnvelopeParameter::DT1) << 4); + data |= env->getParameterValue(FMEnvelopeParameter::ML1); opna_->setRegister(0x30 + bch, data); break; case FMEnvelopeParameter::TL1: - data = static_cast(envFM_[inch]->getParameterValue(FMEnvelopeParameter::TL1)); + data = static_cast(env->getParameterValue(FMEnvelopeParameter::TL1)); // Adjust volume if (mode_ == SongType::FM3chExpanded && inch == 2) { data = calculateTL(2, data); - envFM_[inch]->setParameterValue(FMEnvelopeParameter::TL1, data); // Update + env->setParameterValue(FMEnvelopeParameter::TL1, data); // Update } - else if (isCarrier(0, envFM_[inch]->getParameterValue(FMEnvelopeParameter::AL))) { + else if (IS_CARRIER[0][env->getParameterValue(FMEnvelopeParameter::AL)]) { data = calculateTL(inch, data); - envFM_[inch]->setParameterValue(FMEnvelopeParameter::TL1, data); // Update + env->setParameterValue(FMEnvelopeParameter::TL1, data); // Update } opna_->setRegister(0x40 + bch, data); break; case FMEnvelopeParameter::KS1: case FMEnvelopeParameter::AR1: - data = static_cast(envFM_[inch]->getParameterValue(FMEnvelopeParameter::KS1) << 6); - data |= envFM_[inch]->getParameterValue(FMEnvelopeParameter::AR1); + data = static_cast(env->getParameterValue(FMEnvelopeParameter::KS1) << 6); + data |= env->getParameterValue(FMEnvelopeParameter::AR1); opna_->setRegister(0x50 + bch, data); break; case FMEnvelopeParameter::DR1: @@ -1415,47 +1373,47 @@ else { data = 0; } - data |= envFM_[inch]->getParameterValue(FMEnvelopeParameter::DR1); + data |= env->getParameterValue(FMEnvelopeParameter::DR1); opna_->setRegister(0x60 + bch, data); break; case FMEnvelopeParameter::SR1: - data = static_cast(envFM_[inch]->getParameterValue(FMEnvelopeParameter::SR1)); + data = static_cast(env->getParameterValue(FMEnvelopeParameter::SR1)); opna_->setRegister(0x70 + bch, data); break; case FMEnvelopeParameter::SL1: case FMEnvelopeParameter::RR1: - data = static_cast(envFM_[inch]->getParameterValue(FMEnvelopeParameter::SL1) << 4); - data |= envFM_[inch]->getParameterValue(FMEnvelopeParameter::RR1); + data = static_cast(env->getParameterValue(FMEnvelopeParameter::SL1) << 4); + data |= env->getParameterValue(FMEnvelopeParameter::RR1); opna_->setRegister(0x80 + bch, data); break; case::FMEnvelopeParameter::SSGEG1: - tmp = envFM_[inch]->getParameterValue(FMEnvelopeParameter::SSGEG1); + tmp = env->getParameterValue(FMEnvelopeParameter::SSGEG1); data = (tmp == -1) ? 0 : static_cast(0x08 + tmp); opna_->setRegister(0x90 + bch, data); break; case FMEnvelopeParameter::DT2: case FMEnvelopeParameter::ML2: - data = static_cast(envFM_[inch]->getParameterValue(FMEnvelopeParameter::DT2) << 4); - data |= envFM_[inch]->getParameterValue(FMEnvelopeParameter::ML2); + data = static_cast(env->getParameterValue(FMEnvelopeParameter::DT2) << 4); + data |= env->getParameterValue(FMEnvelopeParameter::ML2); opna_->setRegister(0x30 + bch + 8, data); break; case FMEnvelopeParameter::TL2: - data = static_cast(envFM_[inch]->getParameterValue(FMEnvelopeParameter::TL2)); + data = static_cast(env->getParameterValue(FMEnvelopeParameter::TL2)); // Adjust volume if (mode_ == SongType::FM3chExpanded && inch == 2) { data = calculateTL(6, data); - envFM_[inch]->setParameterValue(FMEnvelopeParameter::TL2, data); // Update + env->setParameterValue(FMEnvelopeParameter::TL2, data); // Update } - else if (isCarrier(1, envFM_[inch]->getParameterValue(FMEnvelopeParameter::AL))) { + else if (IS_CARRIER[1][env->getParameterValue(FMEnvelopeParameter::AL)]) { data = calculateTL(inch, data); - envFM_[inch]->setParameterValue(FMEnvelopeParameter::TL2, data); // Update + env->setParameterValue(FMEnvelopeParameter::TL2, data); // Update } opna_->setRegister(0x40 + bch + 8, data); break; case FMEnvelopeParameter::KS2: case FMEnvelopeParameter::AR2: - data = static_cast(envFM_[inch]->getParameterValue(FMEnvelopeParameter::KS2) << 6); - data |= envFM_[inch]->getParameterValue(FMEnvelopeParameter::AR2); + data = static_cast(env->getParameterValue(FMEnvelopeParameter::KS2) << 6); + data |= env->getParameterValue(FMEnvelopeParameter::AR2); opna_->setRegister(0x50 + bch + 8, data); break; case FMEnvelopeParameter::DR2: @@ -1465,47 +1423,47 @@ else { data = 0; } - data |= envFM_[inch]->getParameterValue(FMEnvelopeParameter::DR2); + data |= env->getParameterValue(FMEnvelopeParameter::DR2); opna_->setRegister(0x60 + bch + 8, data); break; case FMEnvelopeParameter::SR2: - data = static_cast(envFM_[inch]->getParameterValue(FMEnvelopeParameter::SR2)); + data = static_cast(env->getParameterValue(FMEnvelopeParameter::SR2)); opna_->setRegister(0x70 + bch + 8, data); break; case FMEnvelopeParameter::SL2: case FMEnvelopeParameter::RR2: - data = static_cast(envFM_[inch]->getParameterValue(FMEnvelopeParameter::SL2) << 4); - data |= envFM_[inch]->getParameterValue(FMEnvelopeParameter::RR2); + data = static_cast(env->getParameterValue(FMEnvelopeParameter::SL2) << 4); + data |= env->getParameterValue(FMEnvelopeParameter::RR2); opna_->setRegister(0x80 + bch + 8, data); break; case FMEnvelopeParameter::SSGEG2: - tmp = envFM_[inch]->getParameterValue(FMEnvelopeParameter::SSGEG2); + tmp = env->getParameterValue(FMEnvelopeParameter::SSGEG2); data = (tmp == -1) ? 0 : static_cast(0x08 + tmp); opna_->setRegister(0x90 + bch + 8, data); break; case FMEnvelopeParameter::DT3: case FMEnvelopeParameter::ML3: - data = static_cast(envFM_[inch]->getParameterValue(FMEnvelopeParameter::DT3) << 4); - data |= envFM_[inch]->getParameterValue(FMEnvelopeParameter::ML3); + data = static_cast(env->getParameterValue(FMEnvelopeParameter::DT3) << 4); + data |= env->getParameterValue(FMEnvelopeParameter::ML3); opna_->setRegister(0x30 + bch + 4, data); break; case FMEnvelopeParameter::TL3: - data = static_cast(envFM_[inch]->getParameterValue(FMEnvelopeParameter::TL3)); + data = static_cast(env->getParameterValue(FMEnvelopeParameter::TL3)); // Adjust volume if (mode_ == SongType::FM3chExpanded && inch == 2) { data = calculateTL(7, data); - envFM_[inch]->setParameterValue(FMEnvelopeParameter::TL3, data); // Update + env->setParameterValue(FMEnvelopeParameter::TL3, data); // Update } - else if (isCarrier(2, envFM_[inch]->getParameterValue(FMEnvelopeParameter::AL))) { + else if (IS_CARRIER[2][env->getParameterValue(FMEnvelopeParameter::AL)]) { data = calculateTL(inch, data); - envFM_[inch]->setParameterValue(FMEnvelopeParameter::TL3, data); // Update + env->setParameterValue(FMEnvelopeParameter::TL3, data); // Update } opna_->setRegister(0x40 + bch + 4, data); break; case FMEnvelopeParameter::KS3: case FMEnvelopeParameter::AR3: - data = static_cast(envFM_[inch]->getParameterValue(FMEnvelopeParameter::KS3) << 6); - data |= envFM_[inch]->getParameterValue(FMEnvelopeParameter::AR3); + data = static_cast(env->getParameterValue(FMEnvelopeParameter::KS3) << 6); + data |= env->getParameterValue(FMEnvelopeParameter::AR3); opna_->setRegister(0x50 + bch + 4, data); break; case FMEnvelopeParameter::DR3: @@ -1515,47 +1473,47 @@ else { data = 0; } - data |= envFM_[inch]->getParameterValue(FMEnvelopeParameter::DR3); + data |= env->getParameterValue(FMEnvelopeParameter::DR3); opna_->setRegister(0x60 + bch + 4, data); break; case FMEnvelopeParameter::SR3: - data = static_cast(envFM_[inch]->getParameterValue(FMEnvelopeParameter::SR3)); + data = static_cast(env->getParameterValue(FMEnvelopeParameter::SR3)); opna_->setRegister(0x70 + bch + 4, data); break; case FMEnvelopeParameter::SL3: case FMEnvelopeParameter::RR3: - data = static_cast(envFM_[inch]->getParameterValue(FMEnvelopeParameter::SL3) << 4); - data |= envFM_[inch]->getParameterValue(FMEnvelopeParameter::RR3); + data = static_cast(env->getParameterValue(FMEnvelopeParameter::SL3) << 4); + data |= env->getParameterValue(FMEnvelopeParameter::RR3); opna_->setRegister(0x80 + bch + 4, data); break; case FMEnvelopeParameter::SSGEG3: - tmp = envFM_[inch]->getParameterValue(FMEnvelopeParameter::SSGEG3); + tmp = env->getParameterValue(FMEnvelopeParameter::SSGEG3); data = (tmp == -1) ? 0 : static_cast(0x08 + tmp); opna_->setRegister(0x90 + bch + 4, data); break; case FMEnvelopeParameter::DT4: case FMEnvelopeParameter::ML4: - data = static_cast(envFM_[inch]->getParameterValue(FMEnvelopeParameter::DT4) << 4); - data |= envFM_[inch]->getParameterValue(FMEnvelopeParameter::ML4); + data = static_cast(env->getParameterValue(FMEnvelopeParameter::DT4) << 4); + data |= env->getParameterValue(FMEnvelopeParameter::ML4); opna_->setRegister(0x30 + bch + 12, data); break; case FMEnvelopeParameter::TL4: - data = static_cast(envFM_[inch]->getParameterValue(FMEnvelopeParameter::TL4)); + data = static_cast(env->getParameterValue(FMEnvelopeParameter::TL4)); // Adjust volume if (mode_ == SongType::FM3chExpanded && inch == 2) { data = calculateTL(8, data); - envFM_[inch]->setParameterValue(FMEnvelopeParameter::TL4, data); // Update + env->setParameterValue(FMEnvelopeParameter::TL4, data); // Update } else { data = calculateTL(inch, data); - envFM_[inch]->setParameterValue(FMEnvelopeParameter::TL4, data); // Update + env->setParameterValue(FMEnvelopeParameter::TL4, data); // Update } opna_->setRegister(0x40 + bch + 12, data); break; case FMEnvelopeParameter::KS4: case FMEnvelopeParameter::AR4: - data = static_cast(envFM_[inch]->getParameterValue(FMEnvelopeParameter::KS4) << 6); - data |= envFM_[inch]->getParameterValue(FMEnvelopeParameter::AR4); + data = static_cast(env->getParameterValue(FMEnvelopeParameter::KS4) << 6); + data |= env->getParameterValue(FMEnvelopeParameter::AR4); opna_->setRegister(0x50 + bch + 12, data); break; case FMEnvelopeParameter::DR4: @@ -1565,22 +1523,22 @@ else { data = 0; } - data |= envFM_[inch]->getParameterValue(FMEnvelopeParameter::DR4); + data |= env->getParameterValue(FMEnvelopeParameter::DR4); opna_->setRegister(0x60 + bch + 12, data); break; case FMEnvelopeParameter::SR4: - data = static_cast(envFM_[inch]->getParameterValue(FMEnvelopeParameter::SR4)); + data = static_cast(env->getParameterValue(FMEnvelopeParameter::SR4)); opna_->setRegister(0x70 + bch + 12, data); break; case FMEnvelopeParameter::SL4: case FMEnvelopeParameter::RR4: - data = static_cast(envFM_[inch]->getParameterValue(FMEnvelopeParameter::SL4) << 4); - data |= envFM_[inch]->getParameterValue(FMEnvelopeParameter::RR4); + data = static_cast(env->getParameterValue(FMEnvelopeParameter::SL4) << 4); + data |= env->getParameterValue(FMEnvelopeParameter::RR4); opna_->setRegister(0x80 + bch + 12, data); break; case FMEnvelopeParameter::SSGEG4: - tmp = envFM_[inch]->getParameterValue(FMEnvelopeParameter::SSGEG4); - data = judgeSSEGRegisterValue(tmp); + tmp = env->getParameterValue(FMEnvelopeParameter::SSGEG4); + data = judgeSSGEGRegisterValue(tmp); opna_->setRegister(0x90 + bch + 12, data); break; } @@ -1588,51 +1546,36 @@ void OPNAController::restoreFMEnvelopeFromReset(int ch) { - int inch = toInternalFMChannel(ch); + auto& fm = fm_[ch]; + size_t inch = fm.inCh; + auto& inst = refInstFM_[inch]; - if (hasResetEnvFM_[ch] == false || !refInstFM_[inch]) return; + if (fm.hasResetEnv == false || !inst) return; switch (mode_) { case SongType::Standard: { - if (refInstFM_[inch]->getEnvelopeResetEnabled(FMOperatorType::All)) { - hasResetEnvFM_[inch] = false; - writeFMEnveropeParameterToRegister(inch, FMEnvelopeParameter::RR1, - refInstFM_[inch]->getEnvelopeParameter(FMEnvelopeParameter::RR1)); - writeFMEnveropeParameterToRegister(inch, FMEnvelopeParameter::RR2, - refInstFM_[inch]->getEnvelopeParameter(FMEnvelopeParameter::RR2)); - writeFMEnveropeParameterToRegister(inch, FMEnvelopeParameter::RR3, - refInstFM_[inch]->getEnvelopeParameter(FMEnvelopeParameter::RR3)); - writeFMEnveropeParameterToRegister(inch, FMEnvelopeParameter::RR4, - refInstFM_[inch]->getEnvelopeParameter(FMEnvelopeParameter::RR4)); + if (inst->getEnvelopeResetEnabled(FMOperatorType::All)) { + fm.hasResetEnv = false; + for (const FMEnvelopeParameter& rr : PARAM_RR) { + writeFMEnveropeParameterToRegister(inch, rr, inst->getEnvelopeParameter(rr)); + } } break; } case SongType::FM3chExpanded: { - if (refInstFM_[inch]->getEnvelopeResetEnabled(toChannelOperatorType(ch))) { + if (inst->getEnvelopeResetEnabled(fm.opType)) { if (inch == 2) { - FMEnvelopeParameter param; - switch (ch) { - case 2: param = FMEnvelopeParameter::RR1; break; - case 6: param = FMEnvelopeParameter::RR2; break; - case 7: param = FMEnvelopeParameter::RR3; break; - case 8: param = FMEnvelopeParameter::RR4; break; - default: throw std::out_of_range("out of range."); - } - hasResetEnvFM_[ch] = false; + FMEnvelopeParameter param = PARAM_RR[FM3_CH_TO_OP_NUM.at(ch)]; + fm.hasResetEnv = false; writeFMEnveropeParameterToRegister(2, param, refInstFM_[2]->getEnvelopeParameter(param)); } else { - hasResetEnvFM_[inch] = false; - writeFMEnveropeParameterToRegister(inch, FMEnvelopeParameter::RR1, - refInstFM_[inch]->getEnvelopeParameter(FMEnvelopeParameter::RR1)); - writeFMEnveropeParameterToRegister(inch, FMEnvelopeParameter::RR2, - refInstFM_[inch]->getEnvelopeParameter(FMEnvelopeParameter::RR2)); - writeFMEnveropeParameterToRegister(inch, FMEnvelopeParameter::RR3, - refInstFM_[inch]->getEnvelopeParameter(FMEnvelopeParameter::RR3)); - writeFMEnveropeParameterToRegister(inch, FMEnvelopeParameter::RR4, - refInstFM_[inch]->getEnvelopeParameter(FMEnvelopeParameter::RR4)); + fm_[inch].hasResetEnv = false; + for (const FMEnvelopeParameter& rr : PARAM_RR) { + writeFMEnveropeParameterToRegister(inch, rr, inst->getEnvelopeParameter(rr)); + } } } break; @@ -1640,29 +1583,26 @@ } } -void OPNAController::writeFMLFOAllRegisters(int inch) +void OPNAController::writeFMLFOAllRegisters(size_t inch) { if (!refInstFM_[inch]->getLFOEnabled() || lfoStartCntFM_[inch] > 0) { // Clear data uint32_t bch = getFMChannelOffset(inch); // Bank and channel offset opna_->setRegister(0xb4 + bch, static_cast(panFM_[inch] << 6)); - opna_->setRegister(0x60 + bch, static_cast(envFM_[inch]->getParameterValue(FMEnvelopeParameter::DR1))); - opna_->setRegister(0x60 + bch + 8, static_cast(envFM_[inch]->getParameterValue(FMEnvelopeParameter::DR2))); - opna_->setRegister(0x60 + bch + 4, static_cast(envFM_[inch]->getParameterValue(FMEnvelopeParameter::DR3))); - opna_->setRegister(0x60 + bch + 12, static_cast(envFM_[inch]->getParameterValue(FMEnvelopeParameter::DR4))); + for (size_t op = 0; op < 4; ++op) + opna_->setRegister(0x60 + bch + OP_OFFSET[op], + static_cast(envFM_[inch]->getParameterValue(PARAM_DR[op]))); } else { writeFMLFORegister(inch, FMLFOParameter::FREQ); writeFMLFORegister(inch, FMLFOParameter::PMS); writeFMLFORegister(inch, FMLFOParameter::AMS); - writeFMLFORegister(inch, FMLFOParameter::AM1); - writeFMLFORegister(inch, FMLFOParameter::AM2); - writeFMLFORegister(inch, FMLFOParameter::AM3); - writeFMLFORegister(inch, FMLFOParameter::AM4); - lfoStartCntFM_[inch] = -1; + for (const FMLFOParameter& am : PARAM_AM) + writeFMLFORegister(inch, am); + lfoStartCntFM_[inch] = UNUSED_VALUE; } } -void OPNAController::writeFMLFORegister(int inch, FMLFOParameter param) +void OPNAController::writeFMLFORegister(size_t inch, FMLFOParameter param) { uint32_t bch = getFMChannelOffset(inch); // Bank and channel offset uint8_t data; @@ -1704,136 +1644,164 @@ } } -void OPNAController::checkLFOUsed() +void OPNAController::checkLFOUsedByInstrument() { - for (int inch = 0; inch < 6; ++inch) { - if (refInstFM_[inch] && refInstFM_[inch]->getLFOEnabled()) return; + for (const auto& inst : refInstFM_) { + if (inst && inst->getLFOEnabled()) return; } - if (lfoFreq_ != -1) { - lfoFreq_ = -1; - opna_->setRegister(0x22, 0); // LFO off + // Turn off if no instrument uses LFO + if (lfoFreq_ != UNUSED_VALUE) { + lfoFreq_ = UNUSED_VALUE; + opna_->setRegister(0x22, 0); } } -void OPNAController::setFrontFMSequences(int ch) +void OPNAController::setFrontFMSequences(FMChannel& fm) { - if (isMuteFM_[ch]) return; + if (fm.isMute) return; - int inch = toInternalFMChannel(ch); - if (refInstFM_[inch] && refInstFM_[inch]->getLFOEnabled()) { - lfoStartCntFM_[inch] = refInstFM_[inch]->getLFOParameter(FMLFOParameter::Count); + size_t inch = fm.inCh; + auto& inst = refInstFM_[inch]; + if (inst && inst->getLFOEnabled()) { + lfoStartCntFM_[inch] = inst->getLFOParameter(FMLFOParameter::Count); writeFMLFOAllRegisters(inch); } else { - lfoStartCntFM_[inch] = -1; + lfoStartCntFM_[inch] = UNUSED_VALUE; } - checkOperatorSequenceFM(ch, 1); + checkOperatorSequenceFM(fm, 1); - if (treItFM_[ch]) treItFM_[ch]->front(); - sumVolSldFM_[ch] += volSldFM_[ch]; - checkVolumeEffectFM(ch); + if (auto& treItr = fm.treItr) treItr->front(); + fm.volSldSum += fm.volSld; + checkVolumeEffectFM(fm); - if (arpItFM_[ch]) checkRealToneFMByArpeggio(ch, arpItFM_[ch]->front()); - checkPortamentoFM(ch); + if (auto& arpItr = fm.arpItr) { + arpItr->front(); + checkRealToneFMByArpeggio(fm); + } + checkPortamentoFM(fm); - if (ptItFM_[ch]) checkRealToneFMByPitch(ch, ptItFM_[ch]->front()); - if (vibItFM_[ch]) { - vibItFM_[ch]->front(); - needToneSetFM_[ch] = true; + if (auto& ptItr = fm.ptItr) { + ptItr->front(); + checkRealToneFMByPitch(fm); + } + if (auto& vibItr = fm.vibItr) { + vibItr->front(); + fm.shouldSetTone = true; } - if (nsItFM_[ch] && nsItFM_[ch]->front() != -1) { - sumNoteSldFM_[ch] += nsItFM_[ch]->getCommandType(); - needToneSetFM_[ch] = true; + if (auto& nsItr = fm.nsItr) { + nsItr->front(); + if (!nsItr->hasEnded()) { + fm.nsSum += nsItr->data().data; + fm.shouldSetTone = true; + } } - writePitchFM(ch); + writePitchFM(fm); } -void OPNAController::releaseStartFMSequences(int ch) +void OPNAController::releaseStartFMSequences(FMChannel& fm) { - if (isMuteFM_[ch]) return; + if (fm.isMute) return; - int inch = toInternalFMChannel(ch); + size_t inch = fm.inCh; if (lfoStartCntFM_[inch] > 0) { --lfoStartCntFM_[inch]; writeFMLFOAllRegisters(inch); } - checkOperatorSequenceFM(ch, 2); + checkOperatorSequenceFM(fm, 2); - if (treItFM_[ch]) treItFM_[ch]->next(true); - sumVolSldFM_[ch] += volSldFM_[ch]; - checkVolumeEffectFM(ch); + if (auto& treItr = fm.treItr) treItr->release(); + fm.volSldSum += fm.volSld; + checkVolumeEffectFM(fm); - if (arpItFM_[ch]) checkRealToneFMByArpeggio(ch, arpItFM_[ch]->next(true)); - checkPortamentoFM(ch); + if (auto& arpItr = fm.arpItr) { + arpItr->release(); + checkRealToneFMByArpeggio(fm); + } + checkPortamentoFM(fm); - if (ptItFM_[ch]) checkRealToneFMByPitch(ch, ptItFM_[ch]->next(true)); - if (vibItFM_[ch]) { - vibItFM_[ch]->next(true); - needToneSetFM_[ch] = true; + if (auto& ptItr = fm.ptItr) { + ptItr->release(); + checkRealToneFMByPitch(fm); } - if (nsItFM_[ch] && nsItFM_[ch]->next(true) != -1) { - sumNoteSldFM_[ch] += nsItFM_[ch]->getCommandType(); - needToneSetFM_[ch] = true; + if (auto& vibItr = fm.vibItr) { + vibItr->release(); + fm.shouldSetTone = true; + } + if (auto& nsItr = fm.nsItr) { + nsItr->release(); + if (!nsItr->hasEnded()) { + fm.nsSum += nsItr->data().data; + fm.shouldSetTone = true; + } } - if (needToneSetFM_[ch]) writePitchFM(ch); + if (fm.shouldSetTone) writePitchFM(fm); } -void OPNAController::tickEventFM(int ch) +void OPNAController::tickEventFM(FMChannel& fm) { - if (hasPreSetTickEventFM_[ch]) { - hasPreSetTickEventFM_[ch] = false; + if (fm.hasPreSetTickEvent) { + fm.hasPreSetTickEvent = false; } else { - if (isMuteFM_[ch]) return; + if (fm.isMute) return; - int inch = toInternalFMChannel(ch); + size_t inch = fm.inCh; if (lfoStartCntFM_[inch] > 0) { --lfoStartCntFM_[inch]; writeFMLFOAllRegisters(inch); } - checkOperatorSequenceFM(ch, 0); + checkOperatorSequenceFM(fm, 0); - if (treItFM_[ch]) treItFM_[ch]->next(); - sumVolSldFM_[ch] += volSldFM_[ch]; - checkVolumeEffectFM(ch); + if (auto& treItr = fm.treItr) treItr->next(); + fm.volSldSum += fm.volSld; + checkVolumeEffectFM(fm); - if (arpItFM_[ch]) checkRealToneFMByArpeggio(ch, arpItFM_[ch]->next()); - checkPortamentoFM(ch); + if (auto& arpItr = fm.arpItr) { + arpItr->next(); + checkRealToneFMByArpeggio(fm); + } + checkPortamentoFM(fm); - if (ptItFM_[ch]) checkRealToneFMByPitch(ch, ptItFM_[ch]->next()); - if (vibItFM_[ch]) { - vibItFM_[ch]->next(); - needToneSetFM_[ch] = true; + if (auto& ptItr = fm.ptItr) { + ptItr->next(); + checkRealToneFMByPitch(fm); + } + if (auto& vibItr = fm.vibItr) { + vibItr->next(); + fm.shouldSetTone = true; } - if (nsItFM_[ch] && nsItFM_[ch]->next() != -1) { - sumNoteSldFM_[ch] += nsItFM_[ch]->getCommandType(); - needToneSetFM_[ch] = true; + if (auto& nsItr = fm.nsItr) { + nsItr->next(); + if (!nsItr->hasEnded()) { + fm.nsSum += nsItr->data().data; + fm.shouldSetTone = true; + } } - if (needToneSetFM_[ch]) writePitchFM(ch); + if (fm.shouldSetTone) writePitchFM(fm); } } -void OPNAController::checkOperatorSequenceFM(int ch, int type) +void OPNAController::checkOperatorSequenceFM(FMChannel& fm, int type) { - int inch = toInternalFMChannel(ch); - for (auto& p : FM_ENV_PARAMS_OP_.at(toChannelOperatorType(ch))) { + size_t inch = fm.inCh; + for (auto& p : FM_ENV_PARAMS_OP.at(fm.opType)) { if (auto& it = opSeqItFM_[inch].at(p)) { - int t; switch (type) { - case 0: t = it->next(); break; - case 1: t = it->front(); break; - case 2: t = it->next(true); break; + case 0: it->next(); break; + case 1: it->front(); break; + case 2: it->release(); break; default: throw std::out_of_range("The range of type is 0-2."); } - if (t != -1) { - int d = it->getCommandType(); + if (!it->hasEnded()) { + int d = it->data().data; if (d != envFM_[inch]->getParameterValue(p)) { writeFMEnveropeParameterToRegister(inch, p, d); } @@ -1842,271 +1810,214 @@ } } -void OPNAController::checkVolumeEffectFM(int ch) +void OPNAController::checkVolumeEffectFM(FMChannel& fm) { int v; - if (treItFM_[ch]) { - v = treItFM_[ch]->getCommandType() + sumVolSldFM_[ch]; + if (auto& treItr = fm.treItr) { + v = treItr->data().data + fm.volSldSum; } else { - if (volSldFM_[ch]) v = sumVolSldFM_[ch]; + if (fm.volSld) v = fm.volSldSum; else return; } - uint32_t bch = getFMChannelOffset(ch); - int inch = toInternalFMChannel(ch); - switch (toChannelOperatorType(ch)) { + uint32_t bch = getFMChannelOffset(fm.ch); + switch (fm.opType) { case FMOperatorType::All: { - int al = envFM_[ch]->getParameterValue(FMEnvelopeParameter::AL); - if (isCarrier(0, al)) { // Operator 1 - int data = envFM_[ch]->getParameterValue(FMEnvelopeParameter::TL1) + v; - opna_->setRegister(0x40 + bch, static_cast(clamp(data, 0 ,127))); - } - if (isCarrier(1, al)) { // Operator 2 - int data = envFM_[ch]->getParameterValue(FMEnvelopeParameter::TL2) + v; - opna_->setRegister(0x40 + bch + 8, static_cast(clamp(data, 0 ,127))); - } - if (isCarrier(2, al)) { // Operator 3 - int data = envFM_[ch]->getParameterValue(FMEnvelopeParameter::TL3) + v; - opna_->setRegister(0x40 + bch + 4, static_cast(clamp(data, 0 ,127))); - } - if (isCarrier(3, al)) { // Operator 4 - int data = envFM_[ch]->getParameterValue(FMEnvelopeParameter::TL4) + v; - opna_->setRegister(0x40 + bch + 12, static_cast(clamp(data, 0 ,127))); + int al = envFM_[fm.ch]->getParameterValue(FMEnvelopeParameter::AL); + if (IS_CARRIER[0][al]) { // Operator 1 + int data = envFM_[fm.ch]->getParameterValue(FMEnvelopeParameter::TL1) + v; + opna_->setRegister(0x40 + bch, static_cast(utils::clamp(data, 0 ,127))); + } + if (IS_CARRIER[1][al]) { // Operator 2 + int data = envFM_[fm.ch]->getParameterValue(FMEnvelopeParameter::TL2) + v; + opna_->setRegister(0x40 + bch + 8, static_cast(utils::clamp(data, 0 ,127))); + } + if (IS_CARRIER[2][al]) { // Operator 3 + int data = envFM_[fm.ch]->getParameterValue(FMEnvelopeParameter::TL3) + v; + opna_->setRegister(0x40 + bch + 4, static_cast(utils::clamp(data, 0 ,127))); + } + { // Operator 4 (absolutely carrier) + int data = envFM_[fm.ch]->getParameterValue(FMEnvelopeParameter::TL4) + v; + opna_->setRegister(0x40 + bch + 12, static_cast(utils::clamp(data, 0 ,127))); } break; } case FMOperatorType::Op1: { - int data = envFM_[inch]->getParameterValue(FMEnvelopeParameter::TL1) + v; - opna_->setRegister(0x40 + bch, static_cast(clamp(data, 0 ,127))); + int data = envFM_[fm.inCh]->getParameterValue(FMEnvelopeParameter::TL1) + v; + opna_->setRegister(0x40 + bch, static_cast(utils::clamp(data, 0 ,127))); break; } case FMOperatorType::Op2: { - int data = envFM_[inch]->getParameterValue(FMEnvelopeParameter::TL2) + v; - opna_->setRegister(0x40 + bch + 8, static_cast(clamp(data, 0 ,127))); + int data = envFM_[fm.inCh]->getParameterValue(FMEnvelopeParameter::TL2) + v; + opna_->setRegister(0x40 + bch + 8, static_cast(utils::clamp(data, 0 ,127))); break; } case FMOperatorType::Op3: { - int data = envFM_[inch]->getParameterValue(FMEnvelopeParameter::TL3) + v; - opna_->setRegister(0x40 + bch + 4, static_cast(clamp(data, 0 ,127))); + int data = envFM_[fm.inCh]->getParameterValue(FMEnvelopeParameter::TL3) + v; + opna_->setRegister(0x40 + bch + 4, static_cast(utils::clamp(data, 0 ,127))); break; } case FMOperatorType::Op4: { - int data = envFM_[inch]->getParameterValue(FMEnvelopeParameter::TL4) + v; - opna_->setRegister(0x40 + bch + 12, static_cast(clamp(data, 0 ,127))); + int data = envFM_[fm.inCh]->getParameterValue(FMEnvelopeParameter::TL4) + v; + opna_->setRegister(0x40 + bch + 12, static_cast(utils::clamp(data, 0 ,127))); break; } } } -void OPNAController::writePitchFM(int ch) +void OPNAController::writePitchFM(FMChannel& fm) { - if (keyToneFM_[ch].octave == -1) return; // Not set note yet + if (fm.neverSetBaseNote) return; - uint16_t p = calc_pitch::calculateFNumber( - keyToneFM_[ch].note, - keyToneFM_[ch].octave, - keyToneFM_[ch].pitch - + sumPitchFM_[ch] - + (vibItFM_[ch] ? vibItFM_[ch]->getCommandType() : 0) - + detuneFM_[ch] - + sumNoteSldFM_[ch] - + transposeFM_[ch], - fdetuneFM_[ch]); - uint32_t offset = getFMChannelOffset(ch, true); + Note&& note = fm.baseNote + (fm.ptSum + + (fm.vibItr ? fm.vibItr->data().data : 0) + + fm.detune + + fm.nsSum + + fm.transpose); + uint16_t p = note_utils::calculateFNumber(note.getAbsolutePicth(), fm.fdetune); + uint32_t offset = getFMChannelOffset(fm.ch, true); opna_->setRegister(0xa4 + offset, p >> 8); opna_->setRegister(0xa0 + offset, p & 0x00ff); - needToneSetFM_[ch] = false; + fm.shouldSetTone = false; } -void OPNAController::setInstrumentFMProperties(int ch) +void OPNAController::setInstrumentFMProperties(FMChannel& fm) { - int inch = toInternalFMChannel(ch); - FMOperatorType opType = toChannelOperatorType(ch); - enableEnvResetFM_[ch] = refInstFM_[inch]->getEnvelopeResetEnabled(opType); + fm.isEnabledEnvReset = refInstFM_[fm.inCh]->getEnvelopeResetEnabled(fm.opType); } -std::vector OPNAController::getOperatorsInLevel(int level, int al) +uint8_t OPNAController::calculateTL(int ch, uint8_t data) const { - switch (level) { - case 0: - switch (al) { - case 0: - case 1: - case 2: - case 3: - return { 3 }; - case 4: - return { 1, 3 }; - case 5: - case 6: - return { 1, 2, 3 }; - case 7: - return { 0, 1, 2, 3 }; - default: - throw std::invalid_argument("Invalid algorithm."); - } - case 1: - switch (al) { - case 0: - case 1: - return { 2 }; - case 2: - return { 0, 2 }; - case 3: - return { 1, 2 }; - case 4: - return { 0, 2 }; - case 5: - case 6: - return { 0 }; - case 7: - return {}; - default: - throw std::invalid_argument("Invalid algorithm."); - } - case 2: - switch (al) { - case 0: - return { 1 }; - case 1: - return { 0, 1 }; - case 2: - return { 1 }; - case 3: - return { 0 }; - case 4: - case 5: - case 6: - case 7: - return {}; - default: - throw std::invalid_argument("Invalid algorithm."); - } - case 3: - switch (al) { - case 0: - return { 0 }; - case 1: - case 2: - case 3: - case 4: - case 5: - case 6: - case 7: - return {}; - default: - throw std::invalid_argument("Invalid algorithm."); - } - default: - throw std::invalid_argument("Invalid operator level."); - } + auto& fm = fm_[ch]; + int v = (fm.oneshotVol == UNUSED_VALUE) ? fm.baseVol : fm.oneshotVol; + return (data > 127 - v) ? 127 : static_cast(data + v); } //---------- SSG ----------// +namespace +{ +constexpr int AUTO_ENV_SHAPE_TYPE[15] = { 17, 17, 17, 21, 21, 21, 21, 16, 17, 18, 19, 20, 21, 22, 23 }; + +namespace ToneNoiseState +{ +enum : uint8_t +{ + CLEAR_TN = 0, + TONE_TN = 1, + NOISE_TN = 2, + ALL_TN = 3 +}; +} + +inline uint8_t SSGToneFlag(size_t ch) { return (1u << ch); } +inline uint8_t SSGNoiseFlag(size_t ch) { return (8u << ch); } +} + /********** Key on-off **********/ -void OPNAController::keyOnSSG(int ch, Note note, int octave, int pitch, bool isJam) +void OPNAController::keyOnSSG(int ch, const Note& note, bool isJam) { - if (isMuteSSG_[ch]) return; + auto& ssg = ssg_[ch]; + if (ssg.isMute) return; - updateEchoBufferSSG(ch, octave, note, pitch); + ssg.echoBuf.push(note); - if (isTonePrtmSSG_[ch] && hasKeyOnBeforeSSG_[ch]) { - keyToneSSG_[ch].pitch += (sumNoteSldSSG_[ch] +transposeSSG_[ch]); + if (ssg.isTonePrtm && ssg.hasKeyOnBefore) { + ssg.baseNote += (ssg.nsSum + ssg.transpose); } else { - keyToneSSG_[ch] = baseToneSSG_[ch].front(); - sumPitchSSG_[ch] = 0; - sumVolSldSSG_[ch] = 0; - tmpVolSSG_[ch] = -1; - } - if (!noteSldSSGSetFlag_) { - nsItSSG_[ch].reset(); - } - noteSldSSGSetFlag_ = false; - needToneSetSSG_[ch] = true; - sumNoteSldSSG_[ch] = 0; - transposeSSG_[ch] = 0; - - isKeyOnSSG_[ch] = false; // For first tick check - - setFrontSSGSequences(ch); - - hasPreSetTickEventSSG_[ch] = isJam; - isKeyOnSSG_[ch] = true; - hasKeyOnBeforeSSG_[ch] = true; + ssg.baseNote = ssg.echoBuf.latest(); + ssg.neverSetBaseNote = false; + ssg.ptSum = 0; + ssg.volSldSum = 0; + ssg.oneshotVol = UNUSED_VALUE; + } + if (!ssg.hasSetNs) { + ssg.nsItr.reset(); + } + ssg.hasSetNs = false; + ssg.shouldSetTone = true; + ssg.nsSum = 0; + ssg.transpose = 0; + + { + ssg.isInKeyOnProcess_ = true; + setFrontSSGSequences(ssg); + ssg.isInKeyOnProcess_ = false; + } + + ssg.shouldSkip1stTickExec = isJam; + ssg.isKeyOn = true; + ssg.hasKeyOnBefore = true; } void OPNAController::keyOnSSG(int ch, int echoBuf) { - ToneDetail& td = baseToneSSG_[ch].at(static_cast(echoBuf)); - if (td.octave == -1) return; - keyOnSSG(ch, td.note, td.octave, td.pitch); + auto& ssg = ssg_[ch]; + if (static_cast(echoBuf) < ssg.echoBuf.size()) + keyOnSSG(ch, ssg.echoBuf[echoBuf]); } void OPNAController::keyOffSSG(int ch, bool isJam) { - if (!isKeyOnSSG_[ch]) { - tickEventSSG(ch); + auto& ssg = ssg_[ch]; + if (!ssg.isKeyOn) { + tickEventSSG(ssg); return; } - releaseStartSSGSequences(ch); - hasPreSetTickEventSSG_[ch] = isJam; - isKeyOnSSG_[ch] = false; -} - -void OPNAController::updateEchoBufferSSG(int ch, int octave, Note note, int pitch) -{ - baseToneSSG_[ch].pop_back(); - baseToneSSG_[ch].push_front({ octave, note, pitch }); + releaseStartSSGSequences(ssg); + ssg.shouldSkip1stTickExec = isJam; + ssg.isKeyOn = false; } /********** Set instrument **********/ /// NOTE: inst != nullptr void OPNAController::setInstrumentSSG(int ch, std::shared_ptr inst) { - refInstSSG_[ch] = inst; + auto& ssg = ssg_[ch]; + + ssg.refInst = inst; if (inst->getWaveformEnabled()) - wfItSSG_[ch] = inst->getWaveformSequenceIterator(); + ssg.wfItr = inst->getWaveformSequenceIterator(); else - wfItSSG_[ch].reset(); + ssg.wfItr.reset(); if (inst->getToneNoiseEnabled()) - tnItSSG_[ch] = inst->getToneNoiseSequenceIterator(); + ssg.tnItr = inst->getToneNoiseSequenceIterator(); else - tnItSSG_[ch].reset(); + ssg.tnItr.reset(); if (inst->getEnvelopeEnabled()) - envItSSG_[ch] = inst->getEnvelopeSequenceIterator(); + ssg.envItr = inst->getEnvelopeSequenceIterator(); else - envItSSG_[ch].reset(); - if (!isArpEffSSG_[ch]) { + ssg.envItr.reset(); + if (!ssg.isArpEff) { if (inst->getArpeggioEnabled()) - arpItSSG_[ch] = inst->getArpeggioSequenceIterator(); + ssg.arpItr = inst->getArpeggioSequenceIterator(); else - arpItSSG_[ch].reset(); + ssg.arpItr.reset(); } if (inst->getPitchEnabled()) - ptItSSG_[ch] = inst->getPitchSequenceIterator(); + ssg.ptItr = inst->getPitchSequenceIterator(); else - ptItSSG_[ch].reset(); + ssg.ptItr.reset(); } void OPNAController::updateInstrumentSSG(int instNum) { - for (int ch = 0; ch < 3; ++ch) { - if (refInstSSG_[ch] && refInstSSG_[ch]->isRegisteredWithManager() - && refInstSSG_[ch]->getNumber() == instNum) { - if (!refInstSSG_[ch]->getWaveformEnabled()) wfItSSG_[ch].reset(); - if (!refInstSSG_[ch]->getToneNoiseEnabled()) tnItSSG_[ch].reset(); - if (!refInstSSG_[ch]->getEnvelopeEnabled()) envItSSG_[ch].reset(); - if (!refInstSSG_[ch]->getArpeggioEnabled()) arpItSSG_[ch].reset(); - if (!refInstSSG_[ch]->getPitchEnabled()) ptItSSG_[ch].reset(); + for (auto& ssg : ssg_) { + if (ssg.refInst && ssg.refInst->isRegisteredWithManager() + && ssg.refInst->getNumber() == instNum) { + if (!ssg.refInst->getWaveformEnabled()) ssg.wfItr.reset(); + if (!ssg.refInst->getToneNoiseEnabled()) ssg.tnItr.reset(); + if (!ssg.refInst->getEnvelopeEnabled()) ssg.envItr.reset(); + if (!ssg.refInst->getArpeggioEnabled()) ssg.arpItr.reset(); + if (!ssg.refInst->getPitchEnabled()) ssg.ptItr.reset(); } } } @@ -2114,41 +2025,46 @@ /********** Set volume **********/ void OPNAController::setVolumeSSG(int ch, int volume) { - if (volume > 0xf) return; // Out of range - - baseVolSSG_[ch] = volume; - tmpVolSSG_[ch] = -1; + if (volume < bt_defs::NSTEP_SSG_VOLUME) { + auto& ssg = ssg_[ch]; + ssg.baseVol = volume; + ssg.oneshotVol = UNUSED_VALUE; - if (isKeyOnSSG_[ch]) setRealVolumeSSG(ch); + if (ssg.isKeyOn) setRealVolumeSSG(ssg); + } } -void OPNAController::setTemporaryVolumeSSG(int ch, int volume) +void OPNAController::setOneshotVolumeSSG(int ch, int volume) { - if (volume > 0xf) return; // Out of range - - tmpVolSSG_[ch] = volume; + if (volume < bt_defs::NSTEP_SSG_VOLUME) { + auto& ssg = ssg_[ch]; + ssg.oneshotVol = volume; - if (isKeyOnSSG_[ch]) setRealVolumeSSG(ch); + if (ssg.isKeyOn) setRealVolumeSSG(ssg); + } } -void OPNAController::setRealVolumeSSG(int ch) +void OPNAController::setRealVolumeSSG(SSGChannel& ssg) { - if (isBuzzEffSSG_[ch] || isHardEnvSSG_[ch]) return; + if (SSGWaveformType::testHardEnvelopeOccupancity(ssg.wfChState.data) || ssg.isHardEnv) { + ssg.shouldSetEnv = false; + return; + } - int volume = (tmpVolSSG_[ch] == -1) ? baseVolSSG_[ch] : tmpVolSSG_[ch]; - if (envItSSG_[ch]) { - int type = envItSSG_[ch]->getCommandType(); - if (0 <= type && type < 16) { - volume -= (15 - type); + int volume = (ssg.oneshotVol == UNUSED_VALUE) ? ssg.baseVol : ssg.oneshotVol; + if (auto& envItr = ssg.envItr) { + int d = envItr->data().data; + if (0 <= d && d < 16) { + volume -= (15 - d); } } - if (treItSSG_[ch]) volume += treItSSG_[ch]->getCommandType(); - volume += sumVolSldSSG_[ch]; + if (auto& treItr = ssg.treItr) volume += treItr->data().data; + volume += ssg.volSldSum; - volume = clamp(volume, 0, 15); + volume = utils::clamp(volume, 0, 15); - opna_->setRegister(0x08 + static_cast(ch), static_cast(volume)); - needEnvSetSSG_[ch] = false; + opna_->setRegister(0x08 + ssg.ch, static_cast(volume)); + ssg.shouldSetEnv = false; } void OPNAController::setMasterVolumeSSG(double dB) @@ -2159,106 +2075,114 @@ /********** Set effect **********/ void OPNAController::setArpeggioEffectSSG(int ch, int second, int third) { + auto& ssg = ssg_[ch]; if (second || third) { - arpItSSG_[ch] = std::make_unique(second, third); - isArpEffSSG_[ch] = true; + ssg.arpItr = std::make_unique(second, third); + ssg.isArpEff = true; } else { - if (!refInstSSG_[ch] || !refInstSSG_[ch]->getArpeggioEnabled()) arpItSSG_[ch].reset(); - else arpItSSG_[ch] = refInstSSG_[ch]->getArpeggioSequenceIterator(); - isArpEffSSG_[ch] = false; + if (!ssg.refInst || !ssg.refInst->getArpeggioEnabled()) ssg.arpItr.reset(); + else ssg.arpItr = ssg.refInst->getArpeggioSequenceIterator(); + ssg.isArpEff = false; } } void OPNAController::setPortamentoEffectSSG(int ch, int depth, bool isTonePortamento) { - prtmSSG_[ch] = depth; - isTonePrtmSSG_[ch] = depth ? isTonePortamento : false; + auto& ssg = ssg_[ch]; + ssg.prtmDepth = depth; + ssg.isTonePrtm = depth ? isTonePortamento : false; } void OPNAController::setVibratoEffectSSG(int ch, int period, int depth) { - if (period && depth) vibItSSG_[ch] = std::make_unique(period, depth); - else vibItSSG_[ch].reset(); + auto& ssg = ssg_[ch]; + if (period && depth) ssg.vibItr = std::make_unique(period, depth); + else ssg.vibItr.reset(); } void OPNAController::setTremoloEffectSSG(int ch, int period, int depth) { - if (period && depth) treItSSG_[ch] = std::make_unique(period, depth); - else treItSSG_[ch].reset(); + auto& ssg = ssg_[ch]; + if (period && depth) ssg.treItr = std::make_unique(period, depth); + else ssg.treItr.reset(); } void OPNAController::setVolumeSlideSSG(int ch, int depth, bool isUp) { - volSldSSG_[ch] = isUp ? depth : -depth; + ssg_[ch].volSld = isUp ? depth : -depth; } void OPNAController::setDetuneSSG(int ch, int pitch) { - detuneSSG_[ch] = pitch; - needToneSetSSG_[ch] = true; + auto& ssg = ssg_[ch]; + ssg.detune = pitch; + ssg.shouldSetTone = true; } void OPNAController::setFineDetuneSSG(int ch, int pitch) { - fdetuneSSG_[ch] = pitch; - needToneSetSSG_[ch] = true; + auto& ssg = ssg_[ch]; + ssg.fdetune = pitch; + ssg.shouldSetTone = true; } void OPNAController::setNoteSlideSSG(int ch, int speed, int seminote) { + auto& ssg = ssg_[ch]; if (seminote) { - nsItSSG_[ch] = std::make_unique(speed, seminote); - noteSldSSGSetFlag_ = true; + ssg.nsItr = std::make_unique(speed, seminote); + ssg.hasSetNs = true; } - else nsItSSG_[ch].reset(); + else ssg.nsItr.reset(); } void OPNAController::setTransposeEffectSSG(int ch, int seminote) { - transposeSSG_[ch] += (seminote * calc_pitch::SEMINOTE_PITCH); - needToneSetSSG_[ch] = true; + auto& ssg = ssg_[ch]; + ssg.transpose += (seminote * Note::SEMINOTE_PITCH); + ssg.shouldSetTone = true; } void OPNAController::setToneNoiseMixSSG(int ch, int value) { - toneNoiseMixSSG_[ch] = value; + auto& ssg = ssg_[ch]; // Tone - if ((tnSSG_[ch].isTone = (0x01 & value))) mixerSSG_ &= ~SSGToneFlag(ch); - else mixerSSG_ |= SSGToneFlag(ch); + if (value & ToneNoiseState::TONE_TN) mixerSSG_ &= ~SSGToneFlag(ssg.ch); + else mixerSSG_ |= SSGToneFlag(ssg.ch); // Noise - if ((tnSSG_[ch].isNoise = (0x02 & value))) mixerSSG_ &= ~SSGNoiseFlag(ch); - else mixerSSG_ |= SSGNoiseFlag(ch); - opna_->setRegister(0x07, mixerSSG_); + if (value & ToneNoiseState::NOISE_TN) mixerSSG_ &= ~SSGNoiseFlag(ssg.ch); + else mixerSSG_ |= SSGNoiseFlag(ssg.ch); - tnItSSG_[ch].reset(); + ssg.hasRequestedTnEffSet = true; } void OPNAController::setNoisePitchSSG(int ch, int pitch) { - noisePitchSSG_ = pitch; - tnSSG_[ch].noisePeriod_ = pitch; - opna_->setRegister(0x06, static_cast(31 - pitch)); // Reverse order + (void)ch; + noisePeriodSSG_ = 31 - static_cast(pitch); // Reverse order + opna_->setRegister(0x06, noisePeriodSSG_); } void OPNAController::setHardEnvelopePeriod(int ch, bool high, int period) { - bool sendable = isHardEnvSSG_[ch] - && (CommandSequenceUnit::checkDataType(envSSG_[ch].data) == CommandSequenceUnit::RAW); + auto& ssg = ssg_[ch]; + bool sendable = ssg.isHardEnv + && (ssg.envState.type == SSGEnvelopeUnit::RawSubdata); if (high) { hardEnvPeriodHighSSG_ = period; if (sendable) { - envSSG_[ch].data = (period << 8) | (envSSG_[ch].data & 0x00ff); - envSSG_[ch].data |= ~(1 << 16); // raw data flag + int sub = (period << 8) | (ssg.envState.subdata & 0x00ff); + ssg.envState = SSGEnvelopeUnit::makeRawUnit(ssg.envState.data, sub); opna_->setRegister(0x0c, static_cast(period)); } } else { hardEnvPeriodLowSSG_ = period; if (sendable) { - envSSG_[ch].data = (envSSG_[ch].data & 0xff00) | period; - envSSG_[ch].data |= ~(1 << 16); // raw data flag + int sub = (ssg.envState.subdata & 0xff00) | period; + ssg.envState = SSGEnvelopeUnit::makeRawUnit(ssg.envState.data, sub); opna_->setRegister(0x0b, static_cast(period)); } } @@ -2266,875 +2190,861 @@ void OPNAController::setAutoEnvelopeSSG(int ch, int shift, int shape) { + auto& ssg = ssg_[ch]; if (shape) { opna_->setRegister(0x0d, static_cast(shape)); - envSSG_[ch].type = AUTO_ENV_SHAPE_TYPE_[shape - 1]; - opna_->setRegister(0x08 + static_cast(ch), 0x10); - isHardEnvSSG_[ch] = true; + int d = AUTO_ENV_SHAPE_TYPE[shape - 1]; + opna_->setRegister(0x08 + ssg.ch, 0x10); + ssg.isHardEnv = true; if (shift == -8) { // Raw - envSSG_[ch].data = (hardEnvPeriodHighSSG_ << 8) | hardEnvPeriodLowSSG_; + ssg.envState = SSGEnvelopeUnit::makeRawUnit(d, (hardEnvPeriodHighSSG_ << 8) | hardEnvPeriodLowSSG_); opna_->setRegister(0x0c, static_cast(hardEnvPeriodHighSSG_)); opna_->setRegister(0x0b, static_cast(hardEnvPeriodLowSSG_)); + ssg.shouldSetEnv = false; + ssg.shouldSetHardEnvFreq = false; } else { - envSSG_[ch].data = CommandSequenceUnit::shift2data(shift); + ssg.envState = SSGEnvelopeUnit::makeShiftUnit(d, shift); + ssg.shouldSetEnv = true; + ssg.shouldSetHardEnvFreq = true; } } else { - isHardEnvSSG_[ch] = false; - envSSG_[ch] = { -1, CommandSequenceUnit::NODATA }; + ssg.isHardEnv = false; + ssg.envState = SSGEnvelopeUnit(); // Clear hard envelope in setRealVolumeSSG + ssg.shouldSetEnv = true; + ssg.shouldSetHardEnvFreq = false; } - needEnvSetSSG_[ch] = true; - envItSSG_[ch].reset(); + ssg.envItr.reset(); } /********** For state retrieve **********/ void OPNAController::haltSequencesSSG(int ch) { - if (wfItSSG_[ch]) wfItSSG_[ch]->end(); - if (treItSSG_[ch]) treItSSG_[ch]->end(); - if (envItSSG_[ch]) envItSSG_[ch]->end(); - if (tnItSSG_[ch]) tnItSSG_[ch]->end(); - if (arpItSSG_[ch]) arpItSSG_[ch]->end(); - if (ptItSSG_[ch]) ptItSSG_[ch]->end(); - if (vibItSSG_[ch]) vibItSSG_[ch]->end(); - if (nsItSSG_[ch]) nsItSSG_[ch]->end(); + auto& ssg = ssg_[ch]; + if (auto& wfItr = ssg.wfItr) wfItr->end(); + if (auto& treItr = ssg.treItr) treItr->end(); + if (auto& envItr = ssg.envItr) envItr->end(); + if (auto& tnItr = ssg.tnItr) tnItr->end(); + if (auto& arpItr = ssg.arpItr) arpItr->end(); + if (auto& ptItr = ssg.ptItr) ptItr->end(); + if (auto& vibItr = ssg.vibItr) vibItr->end(); + if (auto& nsItr = ssg.nsItr) nsItr->end(); } /********** Chip details **********/ bool OPNAController::isKeyOnSSG(int ch) const { - return isKeyOnSSG_[ch]; + return ssg_[ch].isKeyOn; } bool OPNAController::isTonePortamentoSSG(int ch) const { - return isTonePrtmSSG_[ch]; + return ssg_[ch].isTonePrtm; } -ToneDetail OPNAController::getSSGTone(int ch) const +Note OPNAController::getSSGLatestNote(int ch) const { - return baseToneSSG_[ch].front(); + return ssg_[ch].echoBuf.latest(); } /***********************************/ void OPNAController::initSSG() { mixerSSG_ = 0xff; - opna_->setRegister(0x07, mixerSSG_); // SSG mix - noisePitchSSG_ = 0; + opna_->setRegister(0x07, mixerSSG_); + noisePeriodSSG_ = 0; + opna_->setRegister(0x06, noisePeriodSSG_); hardEnvPeriodHighSSG_ = 0; hardEnvPeriodLowSSG_ = 0; - for (int ch = 0; ch < 3; ++ch) { - isKeyOnSSG_[ch] = false; - hasKeyOnBeforeSSG_[ch] = false; - - refInstSSG_[ch].reset(); // Init envelope - - // Init echo buffer - baseToneSSG_[ch] = std::deque(4); - for (auto& td : baseToneSSG_[ch]) { - td.octave = -1; - } - - keyToneSSG_[ch].note = Note::C; // Dummy - keyToneSSG_[ch].octave = -1; - keyToneSSG_[ch].pitch = 0; // Dummy - sumPitchSSG_[ch] = 0; - tnSSG_[ch] = { false, false, CommandSequenceUnit::NODATA }; - baseVolSSG_[ch] = 0xf; // Init volume - tmpVolSSG_[ch] = -1; - isHardEnvSSG_[ch] = false; - isBuzzEffSSG_[ch] = false; + for (size_t ch = 0; ch < 3; ++ch) { + auto& ssg = ssg_[ch]; + ssg.ch = ch; + + ssg.isKeyOn = false; + ssg.hasKeyOnBefore = false; + ssg.isInKeyOnProcess_ = false; + + ssg.refInst.reset(); // Init envelope + + ssg.echoBuf.clear(); + + ssg.neverSetBaseNote = true; + ssg.baseVol = bt_defs::NSTEP_SSG_VOLUME - 1; // Init volume + ssg.oneshotVol = UNUSED_VALUE; + ssg.isHardEnv = false; // Init sequence - hasPreSetTickEventSSG_[ch] = false; - wfItSSG_[ch].reset(); - wfSSG_[ch] = { SSGWaveformType::UNSET, CommandSequenceUnit::NODATA }; - envItSSG_[ch].reset(); - envSSG_[ch] = { -1, CommandSequenceUnit::NODATA }; - tnItSSG_[ch].reset(); - arpItSSG_[ch].reset(); - ptItSSG_[ch].reset(); - needEnvSetSSG_[ch] = false; - setHardEnvIfNecessary_[ch] = false; - needMixSetSSG_[ch] = false; - needToneSetSSG_[ch] = false; - needSqMaskFreqSetSSG_[ch] = false; + ssg.shouldSkip1stTickExec = false; + ssg.wfItr.reset(); + ssg.wfChState = SSGWaveformUnit::makeOnlyDataUnit(SSGWaveformType::UNSET); + ssg.envItr.reset(); + ssg.envState = SSGEnvelopeUnit(); + ssg.tnItr.reset(); + ssg.arpItr.reset(); + ssg.ptItr.reset(); + ssg.ptSum = 0; + ssg.shouldSetEnv = false; + ssg.shouldSetSqMaskFreq = false; + ssg.shouldSetHardEnvFreq = false; + ssg.shouldUpdateMixState = false; + ssg.shouldSetTone = false; // Effect - isArpEffSSG_[ch] = false; - prtmSSG_[ch] = 0; - isTonePrtmSSG_[ch] = false; - vibItSSG_[ch].reset(); - treItSSG_[ch].reset(); - volSldSSG_[ch] = 0; - sumVolSldSSG_[ch] = 0; - detuneSSG_[ch] = 0; - fdetuneSSG_[ch] = 0; - nsItSSG_[ch].reset(); - sumNoteSldSSG_[ch] = 0; - noteSldSSGSetFlag_ = false; - transposeSSG_[ch] = 0; - toneNoiseMixSSG_[ch] = 0; + ssg.isArpEff = false; + ssg.prtmDepth = 0; + ssg.isTonePrtm = false; + ssg.vibItr.reset(); + ssg.treItr.reset(); + ssg.volSld = 0; + ssg.volSldSum = 0; + ssg.detune = 0; + ssg.fdetune = 0; + ssg.nsItr.reset(); + ssg.nsSum = 0; + ssg.hasSetNs = false; + ssg.transpose = 0; + ssg.hasRequestedTnEffSet = false; } } void OPNAController::setMuteSSGState(int ch, bool isMute) { - isMuteSSG_[ch] = isMute; + auto& ssg = ssg_[ch]; + ssg.isMute = isMute; if (isMute) { - opna_->setRegister(0x08 + static_cast(ch), 0); - isKeyOnSSG_[ch] = false; + opna_->setRegister(0x08 + ssg.ch, 0); + ssg.isKeyOn = false; } } bool OPNAController::isMuteSSG(int ch) { - return isMuteSSG_[ch]; + return ssg_[ch].isMute; } -void OPNAController::setFrontSSGSequences(int ch) +void OPNAController::setFrontSSGSequences(SSGChannel& ssg) { - if (isMuteSSG_[ch]) return; - - setHardEnvIfNecessary_[ch] = false; + if (ssg.isMute) return; - if (wfItSSG_[ch]) writeWaveformSSGToRegister(ch, wfItSSG_[ch]->front()); - else writeSquareWaveform(ch); + if (auto& wfItr = ssg.wfItr) { + wfItr->front(); + writeWaveformSSGToRegister(ssg); + } + else writeSquareWaveform(ssg); - if (treItSSG_[ch]) { - treItSSG_[ch]->front(); - needEnvSetSSG_[ch] = true; + if (auto& treItr = ssg.treItr) { + treItr->front(); + ssg.shouldSetEnv = true; + } + if (ssg.volSld) { + ssg.volSldSum += ssg.volSld; + ssg.shouldSetEnv = true; } - if (volSldSSG_[ch]) { - sumVolSldSSG_[ch] += volSldSSG_[ch]; - needEnvSetSSG_[ch] = true; + if (auto& envItr = ssg.envItr) { + envItr->front(); + writeEnvelopeSSGToRegister(ssg); } - if (envItSSG_[ch]) writeEnvelopeSSGToRegister(ch, envItSSG_[ch]->front()); - else setRealVolumeSSG(ch); + else setRealVolumeSSG(ssg); - if (tnItSSG_[ch]) writeToneNoiseSSGToRegister(ch, tnItSSG_[ch]->front()); - else if (needMixSetSSG_[ch]) writeToneNoiseSSGToRegisterNoReference(ch); + if (ssg.hasRequestedTnEffSet) { // Reflect mixer effect + writeMixerSSGToRegisterByEffect(ssg); + } + else if (auto& tnItr = ssg.tnItr) { + tnItr->front(); + writeMixerSSGToRegisterBySequence(ssg); + } + else if (ssg.shouldUpdateMixState) writeMixerSSGToRegisterByNoReference(ssg); - if (arpItSSG_[ch]) checkRealToneSSGByArpeggio(ch, arpItSSG_[ch]->front()); - checkPortamentoSSG(ch); + if (auto& arpItr = ssg.arpItr) { + arpItr->front(); + checkRealToneSSGByArpeggio(ssg); + } + checkPortamentoSSG(ssg); - if (ptItSSG_[ch]) checkRealToneSSGByPitch(ch, ptItSSG_[ch]->front()); - if (vibItSSG_[ch]) { - vibItSSG_[ch]->front(); - needToneSetSSG_[ch] = true; + if (auto& ptItr = ssg.ptItr) { + ptItr->front(); + checkRealToneSSGByPitch(ssg); } - if (nsItSSG_[ch] && nsItSSG_[ch]->front() != -1) { - sumNoteSldSSG_[ch] += nsItSSG_[ch]->getCommandType(); - needToneSetSSG_[ch] = true; + if (auto& vibItr = ssg.vibItr) { + vibItr->front(); + ssg.shouldSetTone = true; + } + if (auto& nsItr = ssg.nsItr) { + nsItr->front(); + if (!nsItr->hasEnded()) { + ssg.nsSum += nsItr->data().data; + ssg.shouldSetTone = true; + } } - writePitchSSG(ch); + writePitchSSG(ssg); } -void OPNAController::releaseStartSSGSequences(int ch) +void OPNAController::releaseStartSSGSequences(SSGChannel& ssg) { - if (isMuteSSG_[ch]) return; - - setHardEnvIfNecessary_[ch] = false; + if (ssg.isMute) return; - if (wfItSSG_[ch]) writeWaveformSSGToRegister(ch, wfItSSG_[ch]->next(true)); + if (auto& wfItr = ssg.wfItr) { + wfItr->release(); + writeWaveformSSGToRegister(ssg); + } - if (treItSSG_[ch]) { - treItSSG_[ch]->next(true); - needEnvSetSSG_[ch] = true; + if (auto& treItr = ssg.treItr) { + treItr->release(); + ssg.shouldSetEnv = true; } - if (volSldSSG_[ch]) { - sumVolSldSSG_[ch] += volSldSSG_[ch]; - needEnvSetSSG_[ch] = true; + if (ssg.volSld) { + ssg.volSldSum += ssg.volSld; + ssg.shouldSetEnv = true; } - if (envItSSG_[ch]) { - int pos = envItSSG_[ch]->next(true); - if (pos == -1) { - opna_->setRegister(0x08 + static_cast(ch), 0); - isHardEnvSSG_[ch] = false; + if (auto& envItr = ssg.envItr) { + envItr->release(); + if (envItr->hasEnded()) { + // Silence + opna_->setRegister(0x08 + ssg.ch, 0); + ssg.shouldSetEnv = false; + ssg.isHardEnv = false; } - else writeEnvelopeSSGToRegister(ch, pos); + else writeEnvelopeSSGToRegister(ssg); } else { - if (!hasPreSetTickEventSSG_[ch]) { - opna_->setRegister(0x08 + static_cast(ch), 0); - isHardEnvSSG_[ch] = false; - } + // Silence + opna_->setRegister(0x08 + ssg.ch, 0); + ssg.shouldSetEnv = false; + ssg.isHardEnv = false; } - if (tnItSSG_[ch]) writeToneNoiseSSGToRegister(ch, tnItSSG_[ch]->next(true)); - else if (needMixSetSSG_[ch]) writeToneNoiseSSGToRegisterNoReference(ch); + if (ssg.hasRequestedTnEffSet) { // Reflect mixer effect + writeMixerSSGToRegisterByEffect(ssg); + } + else if (auto& tnItr = ssg.tnItr) { + tnItr->release(); + writeMixerSSGToRegisterBySequence(ssg); + } + else if (ssg.shouldUpdateMixState) writeMixerSSGToRegisterByNoReference(ssg); - if (arpItSSG_[ch]) checkRealToneSSGByArpeggio(ch, arpItSSG_[ch]->next(true)); - checkPortamentoSSG(ch); + if (auto& arpItr = ssg.arpItr) { + arpItr->release(); + checkRealToneSSGByArpeggio(ssg); + } + checkPortamentoSSG(ssg); - if (ptItSSG_[ch]) checkRealToneSSGByPitch(ch, ptItSSG_[ch]->next(true)); - if (vibItSSG_[ch]) { - vibItSSG_[ch]->next(true); - needToneSetSSG_[ch] = true; + if (auto& ptItr = ssg.ptItr) { + ptItr->release(); + checkRealToneSSGByPitch(ssg); + } + if (auto& vibItr = ssg.vibItr) { + vibItr->release(); + ssg.shouldSetTone = true; } - if (nsItSSG_[ch] && nsItSSG_[ch]->next(true) != -1) { - sumNoteSldSSG_[ch] += nsItSSG_[ch]->getCommandType(); - needToneSetSSG_[ch] = true; + if (auto& nsItr = ssg.nsItr) { + nsItr->release(); + if (!nsItr->hasEnded()) { + ssg.nsSum += nsItr->data().data; + ssg.shouldSetTone = true; + } } - if (needToneSetSSG_[ch] || (isHardEnvSSG_[ch] && needEnvSetSSG_[ch]) || needSqMaskFreqSetSSG_[ch]) - writePitchSSG(ch); + if (ssg.shouldSetTone || ssg.shouldSetHardEnvFreq || ssg.shouldSetSqMaskFreq) + writePitchSSG(ssg); } -void OPNAController::tickEventSSG(int ch) +void OPNAController::tickEventSSG(SSGChannel& ssg) { - if (hasPreSetTickEventSSG_[ch]) { - hasPreSetTickEventSSG_[ch] = false; + if (ssg.shouldSkip1stTickExec) { + ssg.shouldSkip1stTickExec = false; } else { - if (isMuteSSG_[ch]) return; - - setHardEnvIfNecessary_[ch] = false; + if (ssg.isMute) return; - if (wfItSSG_[ch]) writeWaveformSSGToRegister(ch, wfItSSG_[ch]->next()); + if (auto& wfItr = ssg.wfItr) { + wfItr->next(); + writeWaveformSSGToRegister(ssg); + } - if (treItSSG_[ch]) { - treItSSG_[ch]->next(); - needEnvSetSSG_[ch] = true; + if (auto& treItr = ssg.treItr) { + treItr->next(); + ssg.shouldSetEnv = true; } - if (volSldSSG_[ch]) { - sumVolSldSSG_[ch] += volSldSSG_[ch]; - needEnvSetSSG_[ch] = true; + if (ssg.volSld) { + ssg.volSldSum += ssg.volSld; + ssg.shouldSetEnv = true; } - if (envItSSG_[ch]) { - writeEnvelopeSSGToRegister(ch, envItSSG_[ch]->next()); + if (auto& envItr = ssg.envItr) { + envItr->next(); + writeEnvelopeSSGToRegister(ssg); } - else if (needToneSetSSG_[ch] || needEnvSetSSG_[ch]) { - setRealVolumeSSG(ch); + else if (ssg.shouldSetEnv) { + setRealVolumeSSG(ssg); } - if (tnItSSG_[ch]) writeToneNoiseSSGToRegister(ch, tnItSSG_[ch]->next()); - else if (needMixSetSSG_[ch]) writeToneNoiseSSGToRegisterNoReference(ch); + if (ssg.hasRequestedTnEffSet) { // Reflect mixer effect + writeMixerSSGToRegisterByEffect(ssg); + } + else if (auto& tnItr = ssg.tnItr) { + tnItr->next(); + writeMixerSSGToRegisterBySequence(ssg); + } + else if (ssg.shouldUpdateMixState) writeMixerSSGToRegisterByNoReference(ssg); - if (arpItSSG_[ch]) checkRealToneSSGByArpeggio(ch, arpItSSG_[ch]->next()); - checkPortamentoSSG(ch); + if (auto& arpItr = ssg.arpItr) { + arpItr->next(); + checkRealToneSSGByArpeggio(ssg); + } + checkPortamentoSSG(ssg); - if (ptItSSG_[ch]) checkRealToneSSGByPitch(ch, ptItSSG_[ch]->next()); - if (vibItSSG_[ch]) { - vibItSSG_[ch]->next(); - needToneSetSSG_[ch] = true; + if (auto& ptItr = ssg.ptItr) { + ptItr->next(); + checkRealToneSSGByPitch(ssg); } - if (nsItSSG_[ch] && nsItSSG_[ch]->next() != -1) { - sumNoteSldSSG_[ch] += nsItSSG_[ch]->getCommandType(); - needToneSetSSG_[ch] = true; + if (auto& vibItr = ssg.vibItr) { + vibItr->next(); + ssg.shouldSetTone = true; + } + if (auto& nsItr = ssg.nsItr) { + nsItr->next(); + if (!nsItr->hasEnded()) { + ssg.nsSum += nsItr->data().data; + ssg.shouldSetTone = true; + } } - if (needToneSetSSG_[ch] || (isHardEnvSSG_[ch] && needEnvSetSSG_[ch]) || needSqMaskFreqSetSSG_[ch]) - writePitchSSG(ch); + if (ssg.shouldSetTone || ssg.shouldSetHardEnvFreq || ssg.shouldSetSqMaskFreq) + writePitchSSG(ssg); } } -void OPNAController::writeWaveformSSGToRegister(int ch, int seqPos) +void OPNAController::writeWaveformSSGToRegister(SSGChannel& ssg) { - if (seqPos == -1) return; + auto& wfItr = ssg.wfItr; + if (wfItr->hasEnded()) return; - switch (static_cast(wfItSSG_[ch]->getCommandType())) { + SSGWaveformUnit&& data = wfItr->data(); + switch (data.data) { case SSGWaveformType::SQUARE: { - writeSquareWaveform(ch); + writeSquareWaveform(ssg); return; } case SSGWaveformType::TRIANGLE: { - if (wfSSG_[ch].type == SSGWaveformType::TRIANGLE && isKeyOnSSG_[ch]) return; + if (ssg.wfChState.data == SSGWaveformType::TRIANGLE && !ssg.isInKeyOnProcess_ && ssg.isKeyOn) { + ssg.shouldSetEnv = false; + return; + } - switch (wfSSG_[ch].type) { - case SSGWaveformType::UNSET: - case SSGWaveformType::SQUARE: - case SSGWaveformType::SQM_TRIANGLE: - case SSGWaveformType::SQM_SAW: - case SSGWaveformType::SQM_INVSAW: - needMixSetSSG_[ch] = true; + switch (ssg.wfChState.data) { + case SSGWaveformType::TRIANGLE: + case SSGWaveformType::SAW: + case SSGWaveformType::INVSAW: break; default: + ssg.shouldUpdateMixState = true; break; } - switch (wfSSG_[ch].type) { - case SSGWaveformType::UNSET: - case SSGWaveformType::SQUARE: - case SSGWaveformType::SAW: - case SSGWaveformType::INVSAW: - case SSGWaveformType::SQM_SAW: - case SSGWaveformType::SQM_INVSAW: - opna_->setRegister(0x0d, 0x0e); + // Reset phase + switch (ssg.wfChState.data) { + case SSGWaveformType::TRIANGLE: + case SSGWaveformType::SQM_TRIANGLE: + if (ssg.isInKeyOnProcess_) opna_->setRegister(0x0d, 0x0e); break; default: - if (!isKeyOnSSG_[ch]) opna_->setRegister(0x0d, 0x0e); // First key on + opna_->setRegister(0x0d, 0x0e); break; } - if (isHardEnvSSG_[ch]) { - isBuzzEffSSG_[ch] = true; - isHardEnvSSG_[ch] = false; + if (ssg.isHardEnv) { + ssg.isHardEnv = false; } - else if (!isBuzzEffSSG_[ch] || !isKeyOnSSG_[ch]) { - isBuzzEffSSG_[ch] = true; - opna_->setRegister(0x08 + static_cast(ch), 0x10); + else if (!SSGWaveformType::testHardEnvelopeOccupancity(ssg.wfChState.data) || ssg.isInKeyOnProcess_) { + opna_->setRegister(0x08 + ssg.ch, 0x10); } - if (envSSG_[ch].type == 0) envSSG_[ch] = { -1, CommandSequenceUnit::NODATA }; - - needEnvSetSSG_[ch] = false; - needToneSetSSG_[ch] = true; - needSqMaskFreqSetSSG_[ch] = false; - wfSSG_[ch] = { SSGWaveformType::TRIANGLE, CommandSequenceUnit::NODATA }; - return; + ssg.shouldSetEnv = false; + ssg.shouldSetTone = true; + ssg.shouldSetSqMaskFreq = false; + break; } case SSGWaveformType::SAW: { - if (wfSSG_[ch].type == SSGWaveformType::SAW && isKeyOnSSG_[ch]) return; + if (ssg.wfChState.data == SSGWaveformType::SAW && !ssg.isInKeyOnProcess_ && ssg.isKeyOn) { + ssg.shouldSetEnv = false; + return; + } - switch (wfSSG_[ch].type) { - case SSGWaveformType::UNSET: - case SSGWaveformType::SQUARE: - case SSGWaveformType::SQM_TRIANGLE: - case SSGWaveformType::SQM_SAW: - case SSGWaveformType::SQM_INVSAW: - needMixSetSSG_[ch] = true; + switch (ssg.wfChState.data) { + case SSGWaveformType::TRIANGLE: + case SSGWaveformType::SAW: + case SSGWaveformType::INVSAW: break; default: + ssg.shouldUpdateMixState = true; break; } - switch (wfSSG_[ch].type) { - case SSGWaveformType::UNSET: - case SSGWaveformType::SQUARE: - case SSGWaveformType::TRIANGLE: - case SSGWaveformType::INVSAW: - case SSGWaveformType::SQM_TRIANGLE: - case SSGWaveformType::SQM_INVSAW: - opna_->setRegister(0x0d, 0x0c); + // Reset phase + switch (ssg.wfChState.data) { + case SSGWaveformType::SAW: + case SSGWaveformType::SQM_SAW: + if (ssg.isInKeyOnProcess_) opna_->setRegister(0x0d, 0x0c); break; default: - if (!isKeyOnSSG_[ch]) opna_->setRegister(0x0d, 0x0c); // First key on + opna_->setRegister(0x0d, 0x0c); break; } - if (isHardEnvSSG_[ch]) { - isBuzzEffSSG_[ch] = true; - isHardEnvSSG_[ch] = false; + if (ssg.isHardEnv) { + ssg.isHardEnv = false; } - else if (!isBuzzEffSSG_[ch] || !isKeyOnSSG_[ch]) { - isBuzzEffSSG_[ch] = true; - opna_->setRegister(0x08 + static_cast(ch), 0x10); + else if (!SSGWaveformType::testHardEnvelopeOccupancity(ssg.wfChState.data) || ssg.isInKeyOnProcess_) { + opna_->setRegister(0x08 + ssg.ch, 0x10); } - if (envSSG_[ch].type == 0) envSSG_[ch] = { -1, CommandSequenceUnit::NODATA }; - - needEnvSetSSG_[ch] = false; - needToneSetSSG_[ch] = true; - needSqMaskFreqSetSSG_[ch] = false; - wfSSG_[ch] = { SSGWaveformType::SAW, CommandSequenceUnit::NODATA }; - return; + ssg.shouldSetEnv = false; + ssg.shouldSetTone = true; + ssg.shouldSetSqMaskFreq = false; + break; } case SSGWaveformType::INVSAW: { - if (wfSSG_[ch].type == SSGWaveformType::INVSAW && isKeyOnSSG_[ch]) return; + if (ssg.wfChState.data == SSGWaveformType::INVSAW && !ssg.isInKeyOnProcess_ && ssg.isKeyOn) { + ssg.shouldSetEnv = false; + return; + } - switch (wfSSG_[ch].type) { - case SSGWaveformType::UNSET: - case SSGWaveformType::SQUARE: - case SSGWaveformType::SQM_TRIANGLE: - case SSGWaveformType::SQM_SAW: - case SSGWaveformType::SQM_INVSAW: - needMixSetSSG_[ch] = true; + switch (ssg.wfChState.data) { + case SSGWaveformType::TRIANGLE: + case SSGWaveformType::SAW: + case SSGWaveformType::INVSAW: break; default: + ssg.shouldUpdateMixState = true; break; } - switch (wfSSG_[ch].type) { - case SSGWaveformType::UNSET: - case SSGWaveformType::SQUARE: - case SSGWaveformType::TRIANGLE: - case SSGWaveformType::SAW: - case SSGWaveformType::SQM_TRIANGLE: - case SSGWaveformType::SQM_SAW: - opna_->setRegister(0x0d, 0x08); + // Reset phase + switch (ssg.wfChState.data) { + case SSGWaveformType::INVSAW: + case SSGWaveformType::SQM_INVSAW: + if (ssg.isInKeyOnProcess_) opna_->setRegister(0x0d, 0x08); break; default: - if (!isKeyOnSSG_[ch]) opna_->setRegister(0x0d, 0x08); // First key on + opna_->setRegister(0x0d, 0x08); break; } - if (isHardEnvSSG_[ch]) { - isBuzzEffSSG_[ch] = true; - isHardEnvSSG_[ch] = false; + if (ssg.isHardEnv) { + ssg.isHardEnv = false; } - else if (!isBuzzEffSSG_[ch] || !isKeyOnSSG_[ch]) { - isBuzzEffSSG_[ch] = true; - opna_->setRegister(0x08 + static_cast(ch), 0x10); + else if (!SSGWaveformType::testHardEnvelopeOccupancity(ssg.wfChState.data) || ssg.isInKeyOnProcess_) { + opna_->setRegister(0x08 + ssg.ch, 0x10); } - if (envSSG_[ch].type == 0) envSSG_[ch] = { -1, CommandSequenceUnit::NODATA }; - - needEnvSetSSG_[ch] = false; - needToneSetSSG_[ch] = true; - needSqMaskFreqSetSSG_[ch] = false; - wfSSG_[ch] = { SSGWaveformType::INVSAW, CommandSequenceUnit::NODATA }; - return; + ssg.shouldSetEnv = false; + ssg.shouldSetTone = true; + ssg.shouldSetSqMaskFreq = false; + break; } case SSGWaveformType::SQM_TRIANGLE: { - int data = wfItSSG_[ch]->getCommandData(); - if (wfSSG_[ch].type == SSGWaveformType::SQM_TRIANGLE && wfSSG_[ch].data == data && isKeyOnSSG_[ch]) return; + if (ssg.wfChState == data && !ssg.isInKeyOnProcess_ && ssg.isKeyOn) { + ssg.shouldSetEnv = false; + return; + } - switch (wfSSG_[ch].type) { + switch (ssg.wfChState.data) { case SSGWaveformType::UNSET: case SSGWaveformType::TRIANGLE: case SSGWaveformType::SAW: case SSGWaveformType::INVSAW: - needMixSetSSG_[ch] = true; + ssg.shouldUpdateMixState = true; break; default: break; } - if (wfSSG_[ch].data != data) { - if (CommandSequenceUnit::checkDataType(data) == CommandSequenceUnit::RATIO) { - needSqMaskFreqSetSSG_[ch] = true; - } - else { - uint16_t pitch = static_cast(data); - uint8_t offset = static_cast(ch << 1); + if (ssg.wfChState.subdata != data.subdata) { + if (data.type == SSGWaveformUnit::RatioSubdata) { + // Set frequency of square mask in pitch process since it depends on pitch + ssg.shouldSetSqMaskFreq = true; + } + else { // Raw data + uint16_t pitch = static_cast(data.subdata); + size_t offset = ssg.ch << 1; opna_->setRegister(0x00 + offset, pitch & 0xff); opna_->setRegister(0x01 + offset, pitch >> 8); - needSqMaskFreqSetSSG_[ch] = false; + ssg.shouldSetSqMaskFreq = false; } } else { - needSqMaskFreqSetSSG_[ch] = false; + ssg.shouldSetSqMaskFreq = false; } - switch (wfSSG_[ch].type) { - case SSGWaveformType::UNSET: - case SSGWaveformType::SQUARE: - case SSGWaveformType::SAW: - case SSGWaveformType::INVSAW: - case SSGWaveformType::SQM_SAW: - case SSGWaveformType::SQM_INVSAW: - opna_->setRegister(0x0d, 0x0e); + // Reset phase + switch (ssg.wfChState.data) { + case SSGWaveformType::TRIANGLE: + case SSGWaveformType::SQM_TRIANGLE: + if (ssg.isInKeyOnProcess_) opna_->setRegister(0x0d, 0x0e); break; default: - if (!isKeyOnSSG_[ch]) opna_->setRegister(0x0d, 0x0e); // First key on + opna_->setRegister(0x0d, 0x0e); break; } - if (isHardEnvSSG_[ch]) { - isBuzzEffSSG_[ch] = true; - isHardEnvSSG_[ch] = false; + if (ssg.isHardEnv) { + ssg.isHardEnv = false; } - else if (!isBuzzEffSSG_[ch] || !isKeyOnSSG_[ch]) { - isBuzzEffSSG_[ch] = true; - opna_->setRegister(0x08 + static_cast(ch), 0x10); + else if (!SSGWaveformType::testHardEnvelopeOccupancity(ssg.wfChState.data) || ssg.isInKeyOnProcess_) { + opna_->setRegister(0x08 + ssg.ch, 0x10); } - if (envSSG_[ch].type == 0) envSSG_[ch] = { -1, CommandSequenceUnit::NODATA }; - - needEnvSetSSG_[ch] = false; - needToneSetSSG_[ch] = true; - wfSSG_[ch] = { SSGWaveformType::SQM_TRIANGLE, data }; - return; + ssg.shouldSetEnv = false; + ssg.shouldSetTone = true; + break; } case SSGWaveformType::SQM_SAW: { - int data = wfItSSG_[ch]->getCommandData(); - if (wfSSG_[ch].type == SSGWaveformType::SQM_SAW && wfSSG_[ch].data == data && isKeyOnSSG_[ch]) return; + if (ssg.wfChState == data && !ssg.isInKeyOnProcess_ && ssg.isKeyOn) { + ssg.shouldSetEnv = false; + return; + } - switch (wfSSG_[ch].type) { + switch (ssg.wfChState.data) { case SSGWaveformType::UNSET: case SSGWaveformType::TRIANGLE: case SSGWaveformType::SAW: case SSGWaveformType::INVSAW: - needMixSetSSG_[ch] = true; + ssg.shouldUpdateMixState = true; break; default: break; } - if (wfSSG_[ch].data != data) { - if (CommandSequenceUnit::checkDataType(data) == CommandSequenceUnit::RATIO) { - needSqMaskFreqSetSSG_[ch] = true; - } - else { - uint16_t pitch = static_cast(data); - uint8_t offset = static_cast(ch << 1); + if (ssg.wfChState.subdata != data.subdata) { + if (data.type == SSGWaveformUnit::RatioSubdata) { + // Set frequency of square mask in pitch process since it depends on pitch + ssg.shouldSetSqMaskFreq = true; + } + else { // Raw data + uint16_t pitch = static_cast(data.subdata); + size_t offset = ssg.ch << 1; opna_->setRegister(0x00 + offset, pitch & 0xff); opna_->setRegister(0x01 + offset, pitch >> 8); - needSqMaskFreqSetSSG_[ch] = false; + ssg.shouldSetSqMaskFreq = false; } } else { - needSqMaskFreqSetSSG_[ch] = false; + ssg.shouldSetSqMaskFreq = false; } - switch (wfSSG_[ch].type) { - case SSGWaveformType::UNSET: - case SSGWaveformType::SQUARE: - case SSGWaveformType::TRIANGLE: - case SSGWaveformType::INVSAW: - case SSGWaveformType::SQM_TRIANGLE: - case SSGWaveformType::SQM_INVSAW: - opna_->setRegister(0x0d, 0x0c); + // Reset phase + switch (ssg.wfChState.data) { + case SSGWaveformType::SAW: + case SSGWaveformType::SQM_SAW: + if (ssg.isInKeyOnProcess_) opna_->setRegister(0x0d, 0x0c); break; default: - if (!isKeyOnSSG_[ch]) opna_->setRegister(0x0d, 0x0c); // First key on + opna_->setRegister(0x0d, 0x0c); break; } - if (isHardEnvSSG_[ch]) { - isBuzzEffSSG_[ch] = true; - isHardEnvSSG_[ch] = false; + if (ssg.isHardEnv) { + ssg.isHardEnv = false; } - else if (!isBuzzEffSSG_[ch] || !isKeyOnSSG_[ch]) { - isBuzzEffSSG_[ch] = true; - opna_->setRegister(0x08 + static_cast(ch), 0x10); + else if (!SSGWaveformType::testHardEnvelopeOccupancity(ssg.wfChState.data) || ssg.isInKeyOnProcess_) { + opna_->setRegister(0x08 + ssg.ch, 0x10); } - if (envSSG_[ch].type == 0) envSSG_[ch] = { -1, CommandSequenceUnit::NODATA }; - - needEnvSetSSG_[ch] = false; - needToneSetSSG_[ch] = true; - wfSSG_[ch] = { SSGWaveformType::SQM_SAW, data }; - return; + ssg.shouldSetEnv = false; + ssg.shouldSetTone = true; + break; } case SSGWaveformType::SQM_INVSAW: { - int data = wfItSSG_[ch]->getCommandData(); - if (wfSSG_[ch].type == SSGWaveformType::SQM_INVSAW && wfSSG_[ch].data == data && isKeyOnSSG_[ch]) return; + if (ssg.wfChState == data && !ssg.isInKeyOnProcess_ && ssg.isKeyOn) { + ssg.shouldSetEnv = false; + return; + } - switch (wfSSG_[ch].type) { + switch (ssg.wfChState.data) { case SSGWaveformType::UNSET: case SSGWaveformType::TRIANGLE: case SSGWaveformType::SAW: case SSGWaveformType::INVSAW: - needMixSetSSG_[ch] = true; + ssg.shouldUpdateMixState = true; break; default: break; } - if (wfSSG_[ch].data != data) { - if (CommandSequenceUnit::checkDataType(data) == CommandSequenceUnit::RATIO) { - needSqMaskFreqSetSSG_[ch] = true; - } - else { - uint16_t pitch = static_cast(data); - uint8_t offset = static_cast(ch << 1); + if (ssg.wfChState.subdata != data.subdata) { + if (data.type == SSGWaveformUnit::RatioSubdata) { + // Set frequency of square mask in pitch process since it depends on pitch + ssg.shouldSetSqMaskFreq = true; + } + else { // Raw data + uint16_t pitch = static_cast(data.subdata); + size_t offset = ssg.ch << 1; opna_->setRegister(0x00 + offset, pitch & 0xff); opna_->setRegister(0x01 + offset, pitch >> 8); - needSqMaskFreqSetSSG_[ch] = false; + ssg.shouldSetSqMaskFreq = false; } } else { - needSqMaskFreqSetSSG_[ch] = false; + ssg.shouldSetSqMaskFreq = false; } - switch (wfSSG_[ch].type) { - case SSGWaveformType::UNSET: - case SSGWaveformType::SQUARE: - case SSGWaveformType::TRIANGLE: - case SSGWaveformType::SAW: - case SSGWaveformType::SQM_TRIANGLE: - case SSGWaveformType::SQM_SAW: - opna_->setRegister(0x0d, 0x08); + // Reset phase + switch (ssg.wfChState.data) { + case SSGWaveformType::INVSAW: + case SSGWaveformType::SQM_INVSAW: + if (ssg.isInKeyOnProcess_) opna_->setRegister(0x0d, 0x08); break; default: - if (!isKeyOnSSG_[ch]) opna_->setRegister(0x0d, 0x08); // First key on + opna_->setRegister(0x0d, 0x08); break; } - if (isHardEnvSSG_[ch]) { - isBuzzEffSSG_[ch] = true; - isHardEnvSSG_[ch] = false; + if (ssg.isHardEnv) { + ssg.isHardEnv = false; } - else if (!isBuzzEffSSG_[ch] || !isKeyOnSSG_[ch]) { - isBuzzEffSSG_[ch] = true; - opna_->setRegister(0x08 + static_cast(ch), 0x10); + else if (!SSGWaveformType::testHardEnvelopeOccupancity(ssg.wfChState.data) || ssg.isInKeyOnProcess_) { + opna_->setRegister(0x08 + ssg.ch, 0x10); } - if (envSSG_[ch].type == 0) envSSG_[ch] = { -1, CommandSequenceUnit::NODATA }; - - needEnvSetSSG_[ch] = false; - needToneSetSSG_[ch] = true; - wfSSG_[ch] = { SSGWaveformType::SQM_INVSAW, data }; - return; + ssg.shouldSetEnv = false; + ssg.shouldSetTone = true; + break; } default: - break; + return; } + ssg.wfChState = std::move(data); + + // Clear current envelope state + // since the register of volume and hardware envelope frequency is used by waveform sequence + ssg.envState = SSGEnvelopeUnit(); } -void OPNAController::writeSquareWaveform(int ch) +void OPNAController::writeSquareWaveform(SSGChannel& ssg) { - if (wfSSG_[ch].type == SSGWaveformType::SQUARE) { - if (!isKeyOnSSG_[ch]) { - needEnvSetSSG_[ch] = true; - needToneSetSSG_[ch] = true; + if (ssg.wfChState.data == SSGWaveformType::SQUARE) { + if (ssg.isInKeyOnProcess_) { + ssg.shouldSetEnv = true; + ssg.shouldSetTone = true; } return; } - switch (wfSSG_[ch].type) { + switch (ssg.wfChState.data) { case SSGWaveformType::SQM_TRIANGLE: case SSGWaveformType::SQM_SAW: case SSGWaveformType::SQM_INVSAW: break; default: - { - needMixSetSSG_[ch] = true; + ssg.shouldUpdateMixState = true; break; } + + ssg.shouldSetEnv = true; + ssg.shouldSetTone = true; + ssg.shouldSetSqMaskFreq = false; + ssg.wfChState = SSGWaveformUnit::makeOnlyDataUnit(SSGWaveformType::SQUARE); +} + +void OPNAController::writeEnvelopeSSGToRegister(SSGChannel& ssg) +{ + // Skip if waveform settings use hardware envelope + if (SSGWaveformType::testHardEnvelopeOccupancity(ssg.wfChState.data)) return; + + auto& envItr = ssg.envItr; + if (envItr->hasEnded()) { + if (ssg.shouldSetEnv) setRealVolumeSSG(ssg); + return; } - if (isBuzzEffSSG_[ch]) { - isBuzzEffSSG_[ch] = false; - setHardEnvIfNecessary_[ch] = true; + SSGEnvelopeUnit&& data = envItr->data(); + if (data.data < 16) { // Software envelope + ssg.isHardEnv = false; + ssg.envState = std::move(data); + setRealVolumeSSG(ssg); + } + else { // Hardware envelope + if (!ssg.isHardEnv) { + opna_->setRegister(0x08 + ssg.ch, 0x10); + ssg.isHardEnv = true; + } + if (ssg.envState.subdata != data.subdata) { + ssg.envState.type = data.type; + ssg.envState.subdata = data.subdata; + if (data.type == SSGEnvelopeUnit::RatioSubdata) { + // Set frequency of hardware envelope in pitch process since it depends on pitch + ssg.shouldSetHardEnvFreq = true; + } + else { // Raw data + opna_->setRegister(0x0b, 0x00ff & ssg.envState.subdata); + opna_->setRegister(0x0c, static_cast(ssg.envState.subdata >> 8)); + ssg.shouldSetHardEnvFreq = false; + } + } + if (ssg.envState.data != data.data || ssg.isInKeyOnProcess_) { + opna_->setRegister(0x0d, static_cast(data.data - 16 + 8)); // Reset phase + ssg.envState.data = data.data; + if (data.type == SSGEnvelopeUnit::RatioSubdata) { + // Set frequency of hardware envelope in pitch process since it depends on pitch + ssg.shouldSetHardEnvFreq = true; + } + } } + ssg.shouldSetEnv = false; +} - needEnvSetSSG_[ch] = true; - needToneSetSSG_[ch] = true; - needSqMaskFreqSetSSG_[ch] = false; - wfSSG_[ch] = { SSGWaveformType::SQUARE, CommandSequenceUnit::NODATA }; +void OPNAController::writeMixerSSGToRegisterByEffect(SSGChannel& ssg) +{ + ssg.tnItr.reset(); + opna_->setRegister(0x07, mixerSSG_); + ssg.hasRequestedTnEffSet = false; + ssg.shouldUpdateMixState = false; } -void OPNAController::writeToneNoiseSSGToRegister(int ch, int seqPos) +void OPNAController::writeMixerSSGToRegisterBySequence(SSGChannel& ssg) { - if (seqPos == -1) { - if (needMixSetSSG_[ch]) writeToneNoiseSSGToRegisterNoReference(ch); + auto& tnItr = ssg.tnItr; + if (tnItr->hasEnded()) { + if (ssg.shouldUpdateMixState) writeMixerSSGToRegisterByNoReference(ssg); return; } - int type = tnItSSG_[ch]->getCommandType(); - if (type == -1) return; + int type = tnItr->data().data; uint8_t prevMixer = mixerSSG_; if (!type) { // tone - switch (wfSSG_[ch].type) { + switch (ssg.wfChState.data) { case SSGWaveformType::TRIANGLE: case SSGWaveformType::SAW: case SSGWaveformType::INVSAW: - if (tnSSG_[ch].isTone) { - mixerSSG_ |= SSGToneFlag(ch); - tnSSG_[ch].isTone = false; - } + mixerSSG_ |= SSGToneFlag(ssg.ch); // Off for buzzer effects break; default: - if (!tnSSG_[ch].isTone) { - mixerSSG_ &= ~SSGToneFlag(ch); - tnSSG_[ch].isTone = true; - } + mixerSSG_ &= ~SSGToneFlag(ssg.ch); break; } - - if (tnSSG_[ch].isNoise) { - mixerSSG_ |= SSGNoiseFlag(ch); - tnSSG_[ch].isNoise = false; - tnSSG_[ch].noisePeriod_ = CommandSequenceUnit::NODATA; - } + mixerSSG_ |= SSGNoiseFlag(ssg.ch); } else if (type == 65) { // None - if (tnSSG_[ch].isTone) { - mixerSSG_ |= SSGToneFlag(ch); - tnSSG_[ch].isTone = false; - } - - if (tnSSG_[ch].isNoise) { - mixerSSG_ |= SSGNoiseFlag(ch); - tnSSG_[ch].isNoise = false; - tnSSG_[ch].noisePeriod_ = CommandSequenceUnit::NODATA; - } + mixerSSG_ |= SSGToneFlag(ssg.ch); + mixerSSG_ |= SSGNoiseFlag(ssg.ch); } else if (type > 32) { // Tone&Noise - switch (wfSSG_[ch].type) { + switch (ssg.wfChState.data) { case SSGWaveformType::TRIANGLE: case SSGWaveformType::SAW: case SSGWaveformType::INVSAW: - if (tnSSG_[ch].isTone) { - mixerSSG_ |= SSGToneFlag(ch); - tnSSG_[ch].isTone = false; - } + mixerSSG_ |= SSGToneFlag(ssg.ch); break; default: - if (!tnSSG_[ch].isTone) { - mixerSSG_ &= ~SSGToneFlag(ch); - tnSSG_[ch].isTone = true; - } + mixerSSG_ &= ~SSGToneFlag(ssg.ch); break; } + mixerSSG_ &= ~SSGNoiseFlag(ssg.ch); - if (!tnSSG_[ch].isNoise) { - mixerSSG_ &= ~SSGNoiseFlag(ch); - tnSSG_[ch].isNoise = true; - } - - int p = type - 33; - if (tnSSG_[ch].noisePeriod_ != p) { - opna_->setRegister(0x06, static_cast(31 - p)); // Reverse order - tnSSG_->noisePeriod_ = p; + uint8_t p = static_cast(64 - type); // Reverse order + if (noisePeriodSSG_ != p) { + noisePeriodSSG_ = p; + opna_->setRegister(0x06, p); } } else { // Noise - if (tnSSG_[ch].isTone) { - mixerSSG_ |= SSGToneFlag(ch); - tnSSG_[ch].isTone = false; - } + mixerSSG_ |= SSGToneFlag(ssg.ch); + mixerSSG_ &= ~SSGNoiseFlag(ssg.ch); - if (!tnSSG_[ch].isNoise) { - mixerSSG_ &= ~SSGNoiseFlag(ch); - tnSSG_[ch].isNoise = true; - } - - int p = type - 1; - if (tnSSG_[ch].noisePeriod_ != p) { - opna_->setRegister(0x06, static_cast(31 - p)); // Reverse order - tnSSG_->noisePeriod_ = p; + uint8_t p = static_cast(32 - type); // Reverse order + if (noisePeriodSSG_ != p) { + noisePeriodSSG_ = p; + opna_->setRegister(0x06, p); } } if (mixerSSG_ != prevMixer) opna_->setRegister(0x07, mixerSSG_); - needMixSetSSG_[ch] = false; + ssg.shouldUpdateMixState = false; } -void OPNAController::writeToneNoiseSSGToRegisterNoReference(int ch) +void OPNAController::writeMixerSSGToRegisterByNoReference(SSGChannel& ssg) { - switch (wfSSG_[ch].type) { + switch (ssg.wfChState.data) { case SSGWaveformType::TRIANGLE: case SSGWaveformType::SAW: case SSGWaveformType::INVSAW: - mixerSSG_ |= SSGToneFlag(ch); - tnSSG_[ch].isNoise = false; + mixerSSG_ |= SSGToneFlag(ssg.ch); break; default: - mixerSSG_ &= ~SSGToneFlag(ch); - tnSSG_[ch].isTone = true; + mixerSSG_ &= ~SSGToneFlag(ssg.ch); break; } opna_->setRegister(0x07, mixerSSG_); - - needMixSetSSG_[ch] = false; + ssg.shouldUpdateMixState = false; } -void OPNAController::writeEnvelopeSSGToRegister(int ch, int seqPos) +void OPNAController::writePitchSSG(SSGChannel& ssg) { - if (isBuzzEffSSG_[ch]) return; - if (seqPos == -1) { - if (needEnvSetSSG_[ch]) { - setRealVolumeSSG(ch); - needEnvSetSSG_[ch] = false; - } - return; - } + if (ssg.neverSetBaseNote) return; - int type = envItSSG_[ch]->getCommandType(); - if (type == -1) return; - else if (type < 16) { // Software envelope - isHardEnvSSG_[ch] = false; - envSSG_[ch] = { type, CommandSequenceUnit::NODATA }; - setRealVolumeSSG(ch); - needEnvSetSSG_[ch] = false; - } - else { // Hardware envelope - int data = envItSSG_[ch]->getCommandData(); - if (envSSG_[ch].data != data || setHardEnvIfNecessary_[ch]) { - envSSG_[ch].data = data; - if (CommandSequenceUnit::checkDataType(data) == CommandSequenceUnit::RATIO) { - /* Envelope period is set in writePitchSSG */ - needEnvSetSSG_[ch] = true; - } - else { - opna_->setRegister(0x0b, 0x00ff & envSSG_[ch].data); - opna_->setRegister(0x0c, static_cast(envSSG_[ch].data >> 8)); - needEnvSetSSG_[ch] = false; - } - } - else { - needEnvSetSSG_[ch] = false; - } - if (envSSG_[ch].type != type || !isKeyOnSSG_[ch] || setHardEnvIfNecessary_[ch]) { - opna_->setRegister(0x0d, static_cast(type - 16 + 8)); - envSSG_[ch].type = type; - if (CommandSequenceUnit::checkDataType(data) == CommandSequenceUnit::RATIO) - needEnvSetSSG_[ch] = true; - } - if (!isHardEnvSSG_[ch]) { - opna_->setRegister(static_cast(0x08 + ch), 0x10); - isHardEnvSSG_[ch] = true; - } - // setHardEnvIfNecessary_[ch] = false; - } -} + int p = (ssg.baseNote + (ssg.ptSum + + (ssg.vibItr ? ssg.vibItr->data().data : 0) + + ssg.detune + + ssg.nsSum + + ssg.transpose)).getAbsolutePicth(); -void OPNAController::writePitchSSG(int ch) -{ - if (keyToneSSG_[ch].octave == -1) return; // Not set note yet - - int p = keyToneSSG_[ch].pitch - + sumPitchSSG_[ch] - + (vibItSSG_[ch] ? vibItSSG_[ch]->getCommandType() : 0) - + detuneSSG_[ch] - + sumNoteSldSSG_[ch] - + transposeSSG_[ch]; - - switch (wfSSG_[ch].type) { + switch (ssg.wfChState.data) { case SSGWaveformType::SQUARE: { - uint16_t pitch = calc_pitch::calculateSSGSquareTP( - keyToneSSG_[ch].note, keyToneSSG_[ch].octave, p, fdetuneSSG_[ch]); - if (needToneSetSSG_[ch]) { - uint8_t offset = static_cast(ch << 1); + uint16_t pitch = note_utils::calculateSSGSquareTP(p, ssg.fdetune); + if (ssg.shouldSetTone) { + size_t offset = ssg.ch << 1; opna_->setRegister(0x00 + offset, pitch & 0xff); opna_->setRegister(0x01 + offset, pitch >> 8); - writeAutoEnvelopePitchSSG(ch, pitch); + // Forced call in case of changes in tone processing + writeAutoEnvelopePitchSSG(ssg, pitch); } - else if (isHardEnvSSG_[ch] && needEnvSetSSG_[ch]) { - writeAutoEnvelopePitchSSG(ch, pitch); + else if (ssg.shouldSetHardEnvFreq) { + writeAutoEnvelopePitchSSG(ssg, pitch); } break; } case SSGWaveformType::TRIANGLE: - if (needToneSetSSG_[ch]) { - uint16_t pitch = calc_pitch::calculateSSGTriangleEP( - keyToneSSG_[ch].note, keyToneSSG_[ch].octave, p, fdetuneSSG_[ch]); + if (ssg.shouldSetTone) { + uint16_t pitch = note_utils::calculateSSGTriangleEP(p, ssg.fdetune); opna_->setRegister(0x0b, pitch & 0x00ff); opna_->setRegister(0x0c, pitch >> 8); } break; case SSGWaveformType::SAW: case SSGWaveformType::INVSAW: - if (needToneSetSSG_[ch]){ - uint16_t pitch = calc_pitch::calculateSSGSawEP( - keyToneSSG_[ch].note, keyToneSSG_[ch].octave, p, fdetuneSSG_[ch]); + if (ssg.shouldSetTone){ + uint16_t pitch = note_utils::calculateSSGSawEP(p, ssg.fdetune); opna_->setRegister(0x0b, pitch & 0x00ff); opna_->setRegister(0x0c, pitch >> 8); } break; case SSGWaveformType::SQM_TRIANGLE: { - uint16_t pitch = calc_pitch::calculateSSGTriangleEP( - keyToneSSG_[ch].note, keyToneSSG_[ch].octave, p, fdetuneSSG_[ch]); - if (needToneSetSSG_[ch]) { + uint16_t pitch = note_utils::calculateSSGTriangleEP(p, ssg.fdetune); + if (ssg.shouldSetTone) { opna_->setRegister(0x0b, pitch & 0x00ff); opna_->setRegister(0x0c, pitch >> 8); - if (CommandSequenceUnit::checkDataType(wfSSG_[ch].data) == CommandSequenceUnit::RATIO) { - writeSquareMaskPitchSSG(ch, pitch, true); + // Forced call in case of changes in tone processing + if (ssg.wfChState.type == SSGWaveformUnit::RatioSubdata) { + writeSquareMaskPitchSSG(ssg, pitch, true); } } - else if (needSqMaskFreqSetSSG_[ch]) { - if (CommandSequenceUnit::checkDataType(wfSSG_[ch].data) == CommandSequenceUnit::RATIO) { - writeSquareMaskPitchSSG(ch, pitch, true); + else if (ssg.shouldSetSqMaskFreq) { + if (ssg.wfChState.type == SSGWaveformUnit::RatioSubdata) { + writeSquareMaskPitchSSG(ssg, pitch, true); } } break; @@ -3142,18 +3052,18 @@ case SSGWaveformType::SQM_SAW: case SSGWaveformType::SQM_INVSAW: { - uint16_t pitch = calc_pitch::calculateSSGSawEP( - keyToneSSG_[ch].note, keyToneSSG_[ch].octave, p, fdetuneSSG_[ch]); - if (needToneSetSSG_[ch]) { + uint16_t pitch = note_utils::calculateSSGSawEP(p, ssg.fdetune); + if (ssg.shouldSetTone) { opna_->setRegister(0x0b, pitch & 0x00ff); opna_->setRegister(0x0c, pitch >> 8); - if (CommandSequenceUnit::checkDataType(wfSSG_[ch].data) == CommandSequenceUnit::RATIO) { - writeSquareMaskPitchSSG(ch, pitch, false); + // Forced call in case of changes in tone processing + if (ssg.wfChState.type == SSGWaveformUnit::RatioSubdata) { + writeSquareMaskPitchSSG(ssg, pitch, false); } } - else if (needSqMaskFreqSetSSG_[ch]) { - if (CommandSequenceUnit::checkDataType(wfSSG_[ch].data) == CommandSequenceUnit::RATIO) { - writeSquareMaskPitchSSG(ch, pitch, false); + else if (ssg.shouldSetSqMaskFreq) { + if (ssg.wfChState.type == SSGWaveformUnit::RatioSubdata) { + writeSquareMaskPitchSSG(ssg, pitch, false); } } break; @@ -3162,35 +3072,35 @@ break; } - needToneSetSSG_[ch] = false; - needEnvSetSSG_[ch] = false; - needSqMaskFreqSetSSG_[ch] = false; + ssg.shouldSetTone = false; + ssg.shouldSetEnv = false; + ssg.shouldSetHardEnvFreq = false; + ssg.shouldSetSqMaskFreq = false; } -void OPNAController::writeAutoEnvelopePitchSSG(int ch, double tonePitch) +void OPNAController::writeAutoEnvelopePitchSSG(SSGChannel& ssg, double tonePitch) { // Multiple frequency if triangle - int div = (envSSG_[ch].type == 18 || envSSG_[ch].type == 22) ? 32 : 16; + int div = (ssg.envState.data == 18 || ssg.envState.data == 22) ? 32 : 16; - CommandSequenceUnit::DataType type = CommandSequenceUnit::checkDataType(envSSG_[ch].data); - switch (type) { - case CommandSequenceUnit::RATIO: + switch (ssg.envState.type) { + case SSGEnvelopeUnit::RatioSubdata: { - auto ratio = CommandSequenceUnit::data2ratio(envSSG_[ch].data); - uint16_t period = static_cast(std::round(tonePitch * ratio.first / (ratio.second * div))); + int r1, r2; + ssg.envState.getSubdataAsRatio(r1, r2); + uint16_t period = static_cast(std::round(tonePitch * r1 / (r2 * div))); opna_->setRegister(0x0b, 0x00ff & period); opna_->setRegister(0x0c, static_cast(period >> 8)); break; } - case CommandSequenceUnit::LSHIFT: - case CommandSequenceUnit::RSHIFT: + case SSGEnvelopeUnit::ShiftSubdata: { uint16_t period = static_cast(std::round(tonePitch / div)); - int shift = CommandSequenceUnit::data2shift(envSSG_[ch].data); - shift = (type == CommandSequenceUnit::LSHIFT) ? -shift : shift; - shift -= 4; // Adjust rate to that of 0CC-FamiTracker - if (shift < 0) period <<= -shift; - else period >>= shift; + int rshift; + ssg.envState.getSubdataAsShift(rshift); + rshift -= 4; // Adjust rate to that of 0CC-FamiTracker + if (rshift < 0) period <<= -rshift; + else period >>= rshift; opna_->setRegister(0x0b, 0x00ff & period); opna_->setRegister(0x0c, static_cast(period >> 8)); break; @@ -3200,86 +3110,101 @@ } } -void OPNAController::writeSquareMaskPitchSSG(int ch, double tonePitch, bool isTriangle) +void OPNAController::writeSquareMaskPitchSSG(SSGChannel& ssg, double tonePitch, bool isTriangle) { int mul = isTriangle ? 32 : 16; // Multiple frequency if triangle - auto ratio = CommandSequenceUnit::data2ratio(wfSSG_[ch].data); + int r1, r2; + ssg.wfChState.getSubdataAsRatio(r1, r2); // Calculate mask period - uint16_t period = static_cast(std::round(ratio.first * mul * tonePitch / ratio.second)); - uint8_t offset = static_cast(ch << 1); + uint16_t period = static_cast(std::round(r1 * mul * tonePitch / r2)); + size_t offset = ssg.ch << 1; opna_->setRegister(0x00 + offset, period & 0x00ff); opna_->setRegister(0x01 + offset, period >> 8); } //---------- Rhythm ----------// +namespace +{ +inline uint8_t makePanAndVolumeRegVal(uint8_t panState, int volume) +{ + return static_cast((panState << 6) | volume); +} +} + /********** Key on/off **********/ void OPNAController::setKeyOnFlagRhythm(int ch) { - if (isMuteRhythm_[ch]) return; + auto& rhy = rhythm_[ch]; + if (rhy.isMute) return; - if (tmpVolRhythm_[ch] != -1) - setVolumeRhythm(ch, volRhythm_[ch]); + if (rhy.oneshotVol != UNUSED_VALUE) + setVolumeRhythm(ch, rhy.baseVol); - keyOnFlagRhythm_ |= static_cast(1 << ch); + keyOnRequestFlagsRhythm_ |= static_cast(1 << ch); } void OPNAController::setKeyOffFlagRhythm(int ch) { - keyOffFlagRhythm_ |= static_cast(1 << ch); + keyOffRequestFlagsRhythm_ |= static_cast(1 << ch); } /********** Set volume **********/ void OPNAController::setVolumeRhythm(int ch, int volume) { - if (volume > 0x1f) return; // Out of range - - volRhythm_[ch] = volume; - tmpVolRhythm_[ch] = -1; - opna_->setRegister(0x18 + static_cast(ch), static_cast((panRhythm_[ch] << 6) | volume)); + if (volume < bt_defs::NSTEP_RHYTHM_VOLUME) { + auto& rhy = rhythm_[ch]; + rhy.baseVol = volume; + rhy.oneshotVol = UNUSED_VALUE; + opna_->setRegister(0x18 + static_cast(ch), makePanAndVolumeRegVal(rhy.panState, volume)); + } } -void OPNAController::setMasterVolumeRhythm(int volume) +void OPNAController::setOneshotVolumeRhythm(int ch, int volume) { - mVolRhythm_ = volume; - opna_->setRegister(0x11, static_cast(volume)); + if (volume < bt_defs::NSTEP_RHYTHM_VOLUME) { + auto& rhy = rhythm_[ch]; + rhy.oneshotVol = volume; + opna_->setRegister(0x18 + static_cast(ch), makePanAndVolumeRegVal(rhy.panState, volume)); + } } -void OPNAController::setTemporaryVolumeRhythm(int ch, int volume) +void OPNAController::setMasterVolumeRhythm(int volume) { - if (volume > 0x1f) return; // Out of range - - tmpVolRhythm_[ch] = volume; - opna_->setRegister(0x18 + static_cast(ch), static_cast((panRhythm_[ch] << 6) | volume)); + masterVolRhythm_ = volume; + opna_->setRegister(0x11, static_cast(volume)); } /********** Set effect **********/ void OPNAController::setPanRhythm(int ch, int value) { - panRhythm_[ch] = static_cast(value); - opna_->setRegister(0x18 + static_cast(ch), static_cast((value << 6) | volRhythm_[ch])); + auto& rhy = rhythm_[ch]; + rhy.panState = static_cast(value); + int volume = (rhy.oneshotVol == UNUSED_VALUE) ? rhy.baseVol : rhy.oneshotVol; + opna_->setRegister(0x18 + static_cast(ch), makePanAndVolumeRegVal(value, volume)); } /***********************************/ void OPNAController::initRhythm() { - keyOnFlagRhythm_ = 0; - keyOffFlagRhythm_ = 0; - mVolRhythm_ = 0x3f; + keyOnRequestFlagsRhythm_ = 0; + keyOffRequestFlagsRhythm_ = 0; + masterVolRhythm_ = 0x3f; opna_->setRegister(0x11, 0x3f); // Rhythm total volume - for (int ch = 0; ch < 6; ++ch) { - volRhythm_[ch] = 0x1f; // Init volume - tmpVolRhythm_[ch] = -1; + for (size_t ch = 0; ch < 6; ++ch) { + auto& rhy = rhythm_[ch]; + rhy.baseVol = bt_defs::NSTEP_RHYTHM_VOLUME - 1; + rhy.oneshotVol = UNUSED_VALUE; // Init pan - panRhythm_[ch] = 3; - opna_->setRegister(0x18 + static_cast(ch), 0xdf); + rhy.panState = 3; + opna_->setRegister(0x18 + ch, 0xdf); } } void OPNAController::setMuteRhythmState(int ch, bool isMute) { - isMuteRhythm_[ch] = isMute; + rhythm_[ch].isMute = isMute; if (isMute) { setKeyOffFlagRhythm(ch); @@ -3289,49 +3214,50 @@ bool OPNAController::isMuteRhythm(int ch) { - return isMuteRhythm_[ch]; + return rhythm_[ch].isMute; } void OPNAController::updateKeyOnOffStatusRhythm() { - if (keyOnFlagRhythm_) { - opna_->setRegister(0x10, keyOnFlagRhythm_); - keyOnFlagRhythm_ = 0; - } - if (keyOffFlagRhythm_) { - opna_->setRegister(0x10, 0x80 | keyOffFlagRhythm_); - keyOffFlagRhythm_ = 0; + if (keyOnRequestFlagsRhythm_) { + opna_->setRegister(0x10, keyOnRequestFlagsRhythm_); + keyOnRequestFlagsRhythm_ = 0; + } + if (keyOffRequestFlagsRhythm_) { + opna_->setRegister(0x10, 0x80 | keyOffRequestFlagsRhythm_); + keyOffRequestFlagsRhythm_ = 0; } } //---------- ADPCM ----------// /********** Key on-off **********/ -void OPNAController::keyOnADPCM(Note note, int octave, int pitch, bool isJam) +void OPNAController::keyOnADPCM(const Note& note, bool isJam) { if (isMuteADPCM_ || (!refInstADPCM_ && !refInstKit_)) return; - updateEchoBufferADPCM(octave, note, pitch); + echoBufADPCM_.push(note); - bool isTonePrtm = isTonePrtmADPCM_ && hasKeyOnBeforeADPCM_; + bool isTonePrtm = hasTonePrtmADPCM_ && hasKeyOnBeforeADPCM_; if (isTonePrtm) { - keyToneADPCM_.pitch += (sumNoteSldADPCM_ + transposeADPCM_); + baseNoteADPCM_ += (nsSumADPCM_ + transposeADPCM_); } else { - keyToneADPCM_ = baseToneADPCM_.front(); - sumPitchADPCM_ = 0; - sumVolSldADPCM_ = 0; - tmpVolADPCM_ = -1; - } - if (!noteSldADPCMSetFlag_) { - nsItADPCM_.reset(); - } - noteSldADPCMSetFlag_ = false; - needToneSetADPCM_ = true; - sumNoteSldADPCM_ = 0; + baseNoteADPCM_ = echoBufADPCM_.latest(); + neverSetBaseNoteADPCM_ = false; + ptSumADPCM_ = 0; + volSldSumADPCM_ = 0; + oneshotVolADPCM_ = UNUSED_VALUE; + } + + if (hasSetNsADPCM_) hasSetNsADPCM_ = false; + else nsItADPCM_.reset(); + + shouldSetToneADPCM_ = true; + nsSumADPCM_ = 0; transposeADPCM_ = 0; setFrontADPCMSequences(); - hasPreSetTickEventADPCM_ = isJam; + shouldSkip1stTickExecADPCM_ = isJam; if (!isTonePrtm) { opna_->setRegister(0x101, 0x02); @@ -3343,7 +3269,7 @@ refInstADPCM_->isSampleRepeatable()); } else if (hasStartRequestedKit_) { // valid key in refInstKit_ - int key = octaveAndNoteToNoteNumber(keyToneADPCM_.octave, keyToneADPCM_.note); + int key = baseNoteADPCM_.getNoteNumber(); triggerSamplePlayADPCM(refInstKit_->getSampleStartAddress(key), refInstKit_->getSampleStopAddress(key), refInstKit_->isSampleRepeatable(key)); @@ -3358,9 +3284,8 @@ void OPNAController::keyOnADPCM(int echoBuf) { - ToneDetail& td = baseToneADPCM_.at(static_cast(echoBuf)); - if (td.octave == -1) return; - keyOnADPCM( td.note, td.octave, td.pitch); + if (static_cast(echoBuf) < echoBufADPCM_.size()) + keyOnADPCM(echoBufADPCM_[echoBuf]); } void OPNAController::keyOffADPCM(bool isJam) @@ -3370,16 +3295,10 @@ return; } releaseStartADPCMSequences(); - hasPreSetTickEventADPCM_ = isJam; + shouldSkip1stTickExecADPCM_ = isJam; isKeyOnADPCM_ = false; } -void OPNAController::updateEchoBufferADPCM(int octave, Note note, int pitch) -{ - baseToneADPCM_.pop_back(); - baseToneADPCM_.push_front({ octave, note, pitch }); -} - /********** Set instrument **********/ /// NOTE: inst != nullptr void OPNAController::setInstrumentADPCM(std::shared_ptr inst) @@ -3388,28 +3307,28 @@ refInstKit_.reset(); if (inst->getEnvelopeEnabled()) - envItADPCM_ = inst->getEnvelopeSequenceIterator(); + envItrADPCM_ = inst->getEnvelopeSequenceIterator(); else - envItADPCM_.reset(); - if (!isArpEffADPCM_) { + envItrADPCM_.reset(); + if (!hasArpEffADPCM_) { if (inst->getArpeggioEnabled()) - arpItADPCM_ = inst->getArpeggioSequenceIterator(); + arpItrADPCM_ = inst->getArpeggioSequenceIterator(); else - arpItADPCM_.reset(); + arpItrADPCM_.reset(); } if (inst->getPitchEnabled()) - ptItADPCM_ = inst->getPitchSequenceIterator(); + ptItrADPCM_ = inst->getPitchSequenceIterator(); else - ptItADPCM_.reset(); + ptItrADPCM_.reset(); } void OPNAController::updateInstrumentADPCM(int instNum) { if (refInstADPCM_ && refInstADPCM_->isRegisteredWithManager() && refInstADPCM_->getNumber() == instNum) { - if (!refInstADPCM_->getEnvelopeEnabled()) envItADPCM_.reset(); - if (!refInstADPCM_->getArpeggioEnabled()) arpItADPCM_.reset(); - if (!refInstADPCM_->getPitchEnabled()) ptItADPCM_.reset(); + if (!refInstADPCM_->getEnvelopeEnabled()) envItrADPCM_.reset(); + if (!refInstADPCM_->getArpeggioEnabled()) arpItrADPCM_.reset(); + if (!refInstADPCM_->getPitchEnabled()) ptItrADPCM_.reset(); } } @@ -3419,9 +3338,9 @@ refInstKit_ = inst; refInstADPCM_.reset(); - envItADPCM_.reset(); - arpItADPCM_.reset(); - ptItADPCM_.reset(); + envItrADPCM_.reset(); + arpItrADPCM_.reset(); + ptItrADPCM_.reset(); } void OPNAController::updateInstrumentDrumkit(int instNum, int key) @@ -3437,10 +3356,8 @@ stopAddrADPCM_ = startAddrADPCM_; } -std::vector OPNAController::storeSampleADPCM(std::vector sample) +bool OPNAController::storeSampleADPCM(const std::vector& sample, size_t& startAddr, size_t& stopAddr) { - std::vector addrs(2); - opna_->setRegister(0x110, 0x80); opna_->setRegister(0x100, 0x61); opna_->setRegister(0x100, 0x60); @@ -3450,72 +3367,72 @@ opna_->setRegister(0x10c, dramLim & 0xff); opna_->setRegister(0x10d, (dramLim >> 8) & 0xff); + bool stored = false; if (storePointADPCM_ < dramLim) { - size_t startAddress = storePointADPCM_; - opna_->setRegister(0x102, startAddress & 0xff); - opna_->setRegister(0x103, (startAddress >> 8) & 0xff); - - size_t stopAddress = startAddress + ((sample.size() - 1) >> 5); // By 32 bytes - stopAddress = std::min(stopAddress, dramLim); - opna_->setRegister(0x104, stopAddress & 0xff); - opna_->setRegister(0x105, (stopAddress >> 8) & 0xff); - storePointADPCM_ = stopAddress + 1; + startAddr = storePointADPCM_; + opna_->setRegister(0x102, startAddr & 0xff); + opna_->setRegister(0x103, (startAddr >> 8) & 0xff); + + stopAddr = startAddr + ((sample.size() - 1) >> 5); // By 32 bytes + stopAddr = std::min(stopAddr, dramLim); + opna_->setRegister(0x104, stopAddr & 0xff); + opna_->setRegister(0x105, (stopAddr >> 8) & 0xff); + storePointADPCM_ = stopAddr + 1; size_t size = sample.size(); for (size_t i = 0; i < size; ++i) { opna_->setRegister(0x108, sample[i]); } - - addrs = { startAddress, stopAddress }; + stored = true; } opna_->setRegister(0x100, 0x00); opna_->setRegister(0x110, 0x80); - return addrs; + return stored; } /********** Set volume **********/ void OPNAController::setVolumeADPCM(int volume) { - if (volume > 0xff) return; // Out of range - - baseVolADPCM_ = volume; - tmpVolADPCM_ = -1; + if (volume < bt_defs::NSTEP_ADPCM_VOLUME) { + baseVolADPCM_ = volume; + oneshotVolADPCM_ = UNUSED_VALUE; - if (isKeyOnADPCM_) setRealVolumeADPCM(); + if (isKeyOnADPCM_) setRealVolumeADPCM(); + } } -void OPNAController::setTemporaryVolumeADPCM(int volume) +void OPNAController::setOneshotVolumeADPCM(int volume) { - if (volume > 0xff) return; // Out of range - - tmpVolADPCM_ = volume; + if (volume < bt_defs::NSTEP_ADPCM_VOLUME) { + oneshotVolADPCM_ = volume; - if (isKeyOnADPCM_) setRealVolumeADPCM(); + if (isKeyOnADPCM_) setRealVolumeADPCM(); + } } void OPNAController::setRealVolumeADPCM() { - int volume = (tmpVolADPCM_ == -1) ? baseVolADPCM_ : tmpVolADPCM_; - if (envItADPCM_) { - int type = envItADPCM_->getCommandType(); - if (type >= 0) volume -= (0xff - type); + int volume = (oneshotVolADPCM_ == UNUSED_VALUE) ? baseVolADPCM_ : oneshotVolADPCM_; + if (envItrADPCM_) { + int type = envItrADPCM_->data().data; + if (type >= 0) volume -= (bt_defs::NSTEP_ADPCM_VOLUME - 1 - type); } - if (treItADPCM_) volume += treItADPCM_->getCommandType(); - volume += sumVolSldADPCM_; + if (treItrADPCM_) volume += treItrADPCM_->data().data; + volume += volSldSumADPCM_; - volume = clamp(volume, 0, 0xff); + volume = utils::clamp(volume, 0, bt_defs::NSTEP_ADPCM_VOLUME - 1); opna_->setRegister(0x10b, static_cast(volume)); - needEnvSetADPCM_ = false; + shouldWriteEnvADPCM_ = false; } /********** Set effect **********/ void OPNAController::setPanADPCM(int value) { - panADPCM_ = static_cast(value << 6); - opna_->setRegister(0x101, panADPCM_ | 0x02); + panStateADPCM_ = static_cast(value << 6); + opna_->setRegister(0x101, panStateADPCM_ | 0x02); } void OPNAController::setArpeggioEffectADPCM(int second, int third) @@ -3523,13 +3440,13 @@ if (refInstKit_) return; if (second || third) { - arpItADPCM_ = std::make_unique(second, third); - isArpEffADPCM_ = true; + arpItrADPCM_ = std::make_unique(second, third); + hasArpEffADPCM_ = true; } else { - if (!refInstADPCM_ || !refInstADPCM_->getArpeggioEnabled()) arpItADPCM_.reset(); - else arpItADPCM_ = refInstADPCM_->getArpeggioSequenceIterator(); - isArpEffADPCM_ = false; + if (!refInstADPCM_ || !refInstADPCM_->getArpeggioEnabled()) arpItrADPCM_.reset(); + else arpItrADPCM_ = refInstADPCM_->getArpeggioSequenceIterator(); + hasArpEffADPCM_ = false; } } @@ -3537,22 +3454,22 @@ { if (refInstKit_) return; - prtmADPCM_ = depth; - isTonePrtmADPCM_ = depth ? isTonePortamento : false; + prtmDepthADPCM_ = depth; + hasTonePrtmADPCM_ = depth ? isTonePortamento : false; } void OPNAController::setVibratoEffectADPCM(int period, int depth) { if (refInstKit_) return; - if (period && depth) vibItADPCM_ = std::make_unique(period, depth); - else vibItADPCM_.reset(); + if (period && depth) vibItrADPCM_ = std::make_unique(period, depth); + else vibItrADPCM_.reset(); } void OPNAController::setTremoloEffectADPCM(int period, int depth) { - if (period && depth) treItADPCM_ = std::make_unique(period, depth); - else treItADPCM_.reset(); + if (period && depth) treItrADPCM_ = std::make_unique(period, depth); + else treItrADPCM_.reset(); } void OPNAController::setVolumeSlideADPCM(int depth, bool isUp) @@ -3565,7 +3482,7 @@ if (refInstKit_) return; detuneADPCM_ = pitch; - needToneSetADPCM_ = true; + shouldSetToneADPCM_ = true; } void OPNAController::setFineDetuneADPCM(int pitch) @@ -3573,7 +3490,7 @@ if (refInstKit_) return; fdetuneADPCM_ = pitch; - needToneSetADPCM_ = true; + shouldSetToneADPCM_ = true; } void OPNAController::setNoteSlideADPCM(int speed, int seminote) @@ -3582,25 +3499,25 @@ if (seminote) { nsItADPCM_ = std::make_unique(speed, seminote); - noteSldADPCMSetFlag_ = true; + hasSetNsADPCM_ = true; } else nsItADPCM_.reset(); } void OPNAController::setTransposeEffectADPCM(int seminote) { - transposeADPCM_ += (seminote * calc_pitch::SEMINOTE_PITCH); - needToneSetADPCM_ = true; + transposeADPCM_ += (seminote * Note::SEMINOTE_PITCH); + shouldSetToneADPCM_ = true; } /********** For state retrieve **********/ void OPNAController::haltSequencesADPCM() { - if (treItADPCM_) treItADPCM_->end(); - if (envItADPCM_) envItADPCM_->end(); - if (arpItADPCM_) arpItADPCM_->end(); - if (ptItADPCM_) ptItADPCM_->end(); - if (vibItADPCM_) vibItADPCM_->end(); + if (treItrADPCM_) treItrADPCM_->end(); + if (envItrADPCM_) envItrADPCM_->end(); + if (arpItrADPCM_) arpItrADPCM_->end(); + if (ptItrADPCM_) ptItrADPCM_->end(); + if (vibItrADPCM_) vibItrADPCM_->end(); if (nsItADPCM_) nsItADPCM_->end(); } @@ -3612,12 +3529,12 @@ bool OPNAController::isTonePortamentoADPCM() const { - return isTonePrtmADPCM_; + return hasTonePrtmADPCM_; } -ToneDetail OPNAController::getADPCMTone() const +Note OPNAController::getADPCMLatestNote() const { - return baseToneADPCM_.front(); + return echoBufADPCM_.latest(); } size_t OPNAController::getADPCMStoredSize() const @@ -3634,44 +3551,38 @@ refInstADPCM_.reset(); // Init envelope refInstKit_.reset(); - // Init echo buffer - baseToneADPCM_ = std::deque(4); - for (auto& td : baseToneADPCM_) { - td.octave = -1; - } + echoBufADPCM_.clear(); - keyToneADPCM_.note = Note::C; // Dummy - keyToneADPCM_.octave = -1; - keyToneADPCM_.pitch = 0; // Dummy - sumPitchADPCM_ = 0; - baseVolADPCM_ = 0xff; // Init volume - tmpVolADPCM_ = -1; - panADPCM_ = 0xc0; + neverSetBaseNoteADPCM_ = true; + baseVolADPCM_ = bt_defs::NSTEP_ADPCM_VOLUME - 1; // Init volume + oneshotVolADPCM_ = UNUSED_VALUE; + panStateADPCM_ = 0xc0; + shouldWriteEnvADPCM_ = false; + shouldSetToneADPCM_ = false; startAddrADPCM_ = std::numeric_limits::max(); stopAddrADPCM_ = startAddrADPCM_; hasStartRequestedKit_ = false; // Init sequence - hasPreSetTickEventADPCM_ = false; - envItADPCM_.reset(); - arpItADPCM_.reset(); - ptItADPCM_.reset(); - needEnvSetADPCM_ = false; - needToneSetADPCM_ = false; + shouldSkip1stTickExecADPCM_ = false; + envItrADPCM_.reset(); + arpItrADPCM_.reset(); + ptItrADPCM_.reset(); + ptSumADPCM_ = 0; // Effect - isArpEffADPCM_ = false; - prtmADPCM_ = 0; - isTonePrtmADPCM_ = false; - vibItADPCM_.reset(); - treItADPCM_.reset(); + hasArpEffADPCM_ = false; + prtmDepthADPCM_ = 0; + hasTonePrtmADPCM_ = false; + vibItrADPCM_.reset(); + treItrADPCM_.reset(); volSldADPCM_ = 0; - sumVolSldADPCM_ = 0; + volSldSumADPCM_ = 0; detuneADPCM_ = 0; fdetuneADPCM_ = 0; nsItADPCM_.reset(); - sumNoteSldADPCM_ = 0; - noteSldADPCMSetFlag_ = false; + nsSumADPCM_ = 0; + hasSetNsADPCM_ = false; transposeADPCM_ = 0; opna_->setRegister(0x100, 0xa1); // Stop synthesis @@ -3700,28 +3611,40 @@ { if (isMuteADPCM_ || (!refInstADPCM_ && !refInstKit_)) return; - if (treItADPCM_) { - treItADPCM_->front(); - needEnvSetADPCM_ = true; + if (treItrADPCM_) { + treItrADPCM_->front(); + shouldWriteEnvADPCM_ = true; } if (volSldADPCM_) { - sumVolSldADPCM_ += volSldADPCM_; - needEnvSetADPCM_ = true; + volSldSumADPCM_ += volSldADPCM_; + shouldWriteEnvADPCM_ = true; + } + if (envItrADPCM_) { + envItrADPCM_->front(); + writeEnvelopeADPCMToRegister(); } - if (envItADPCM_) writeEnvelopeADPCMToRegister(envItADPCM_->front()); else setRealVolumeADPCM(); - if (arpItADPCM_) checkRealToneADPCMByArpeggio(arpItADPCM_->front()); + if (arpItrADPCM_) { + arpItrADPCM_->front(); + checkRealToneADPCMByArpeggio(); + } checkPortamentoADPCM(); - if (ptItADPCM_) checkRealToneADPCMByPitch(ptItADPCM_->front()); - if (vibItADPCM_) { - vibItADPCM_->front(); - needToneSetADPCM_ = true; - } - if (nsItADPCM_ && nsItADPCM_->front() != -1) { - sumNoteSldADPCM_ += nsItADPCM_->getCommandType(); - needToneSetADPCM_ = true; + if (ptItrADPCM_) { + ptItrADPCM_->front(); + checkRealToneADPCMByPitch(); + } + if (vibItrADPCM_) { + vibItrADPCM_->front(); + /* shouldSetToneADPCM_ = true; */ + } + if (nsItADPCM_) { + nsItADPCM_->front(); + if (!nsItADPCM_->hasEnded()) { + nsSumADPCM_ += nsItADPCM_->data().data; + /* shouldSetToneADPCM_ = true; */ + } } writePitchADPCM(); @@ -3731,88 +3654,107 @@ { if (isMuteADPCM_ || (!refInstADPCM_ && !refInstKit_)) return; - if (treItADPCM_) { - treItADPCM_->next(true); - needEnvSetADPCM_ = true; + if (treItrADPCM_) { + treItrADPCM_->release(); + shouldWriteEnvADPCM_ = true; } if (volSldADPCM_) { - sumVolSldADPCM_ += volSldADPCM_; - needEnvSetADPCM_ = true; + volSldSumADPCM_ += volSldADPCM_; + shouldWriteEnvADPCM_ = true; } - if (envItADPCM_) { - int pos = envItADPCM_->next(true); - if (pos == -1) { - opna_->setRegister(0x10b, 0); + if (envItrADPCM_) { + envItrADPCM_->release(); + if (!envItrADPCM_->hasEnded()) { + opna_->setRegister(0x10b, 0); // Silence + shouldWriteEnvADPCM_ = false; } - else writeEnvelopeADPCMToRegister(pos); + else writeEnvelopeADPCMToRegister(); } else { - if (!hasPreSetTickEventADPCM_) { - opna_->setRegister(0x10b, 0); - } + opna_->setRegister(0x10b, 0); // Silence + shouldWriteEnvADPCM_ = false; } - if (arpItADPCM_) checkRealToneADPCMByArpeggio(arpItADPCM_->next(true)); + if (arpItrADPCM_) { + arpItrADPCM_->release(); + checkRealToneADPCMByArpeggio(); + } checkPortamentoADPCM(); - if (ptItADPCM_) checkRealToneADPCMByPitch(ptItADPCM_->next(true)); - if (vibItADPCM_) { - vibItADPCM_->next(true); - needToneSetADPCM_ = true; + if (ptItrADPCM_) { + ptItrADPCM_->release(); + checkRealToneADPCMByPitch(); + } + if (vibItrADPCM_) { + vibItrADPCM_->release(); + shouldSetToneADPCM_ = true; } - if (nsItADPCM_ && nsItADPCM_->next(true) != -1) { - sumNoteSldADPCM_ += nsItADPCM_->getCommandType(); - needToneSetADPCM_ = true; + if (nsItADPCM_) { + nsItADPCM_->release(); + if (!nsItADPCM_->hasEnded()) { + nsSumADPCM_ += nsItADPCM_->data().data; + shouldSetToneADPCM_ = true; + } } - if (needToneSetADPCM_) writePitchADPCM(); + if (shouldSetToneADPCM_) writePitchADPCM(); hasStartRequestedKit_ = false; // Always silent at relase in drumkit } void OPNAController::tickEventADPCM() { - if (hasPreSetTickEventADPCM_) { - hasPreSetTickEventADPCM_ = false; + if (shouldSkip1stTickExecADPCM_) { + shouldSkip1stTickExecADPCM_ = false; } else { if (isMuteADPCM_ || (!refInstADPCM_ && !refInstKit_)) return; - if (treItADPCM_) { - treItADPCM_->next(); - needEnvSetADPCM_ = true; + if (treItrADPCM_) { + treItrADPCM_->next(); + shouldWriteEnvADPCM_ = true; } if (volSldADPCM_) { - sumVolSldADPCM_ += volSldADPCM_; - needEnvSetADPCM_ = true; + volSldSumADPCM_ += volSldADPCM_; + shouldWriteEnvADPCM_ = true; } - if (envItADPCM_) { - writeEnvelopeADPCMToRegister(envItADPCM_->next()); + if (envItrADPCM_) { + envItrADPCM_->next(); + writeEnvelopeADPCMToRegister(); } - else if (needEnvSetADPCM_) { + else if (shouldWriteEnvADPCM_) { setRealVolumeADPCM(); } - if (arpItADPCM_) checkRealToneADPCMByArpeggio(arpItADPCM_->next()); + if (arpItrADPCM_) { + arpItrADPCM_->next(); + checkRealToneADPCMByArpeggio(); + } checkPortamentoADPCM(); - if (ptItADPCM_) checkRealToneADPCMByPitch(ptItADPCM_->next()); - if (vibItADPCM_) { - vibItADPCM_->next(); - needToneSetADPCM_ = true; + if (ptItrADPCM_) { + ptItrADPCM_->next(); + checkRealToneADPCMByPitch(); } - if (nsItADPCM_ && nsItADPCM_->next() != -1) { - sumNoteSldADPCM_ += nsItADPCM_->getCommandType(); - needToneSetADPCM_ = true; + if (vibItrADPCM_) { + vibItrADPCM_->next(); + shouldSetToneADPCM_ = true; + } + if (nsItADPCM_) { + nsItADPCM_->next(); + if (!nsItADPCM_->hasEnded()) { + nsSumADPCM_ += nsItADPCM_->data().data; + shouldSetToneADPCM_ = true; + } } - if (needToneSetADPCM_) writePitchADPCM(); + if (shouldSetToneADPCM_) writePitchADPCM(); if (hasStartRequestedKit_) { opna_->setRegister(0x101, 0x02); opna_->setRegister(0x100, 0xa1); - int key = octaveAndNoteToNoteNumber(keyToneADPCM_.octave, keyToneADPCM_.note); + int key = baseNoteADPCM_.getNoteNumber(); triggerSamplePlayADPCM(refInstKit_->getSampleStartAddress(key), refInstKit_->getSampleStopAddress(key), refInstKit_->isSampleRepeatable(key)); @@ -3821,42 +3763,37 @@ } } -void OPNAController::writeEnvelopeADPCMToRegister(int seqPos) +void OPNAController::writeEnvelopeADPCMToRegister() { - if (seqPos != -1 || needEnvSetADPCM_) { + if (!envItrADPCM_->hasEnded() || shouldWriteEnvADPCM_) { setRealVolumeADPCM(); - needEnvSetADPCM_ = false; } } void OPNAController::writePitchADPCM() { - if (keyToneADPCM_.octave == -1) return; // Not set note yet + if (neverSetBaseNoteADPCM_) return; if (refInstADPCM_) { - int p = keyToneADPCM_.pitch - + sumPitchADPCM_ - + (vibItADPCM_ ? vibItADPCM_->getCommandType() : 0) - + detuneADPCM_ - + sumNoteSldADPCM_ - + transposeADPCM_; - p = calc_pitch::calculatePitchIndex(keyToneADPCM_.octave, keyToneADPCM_.note, p); - - int diff = p - calc_pitch::SEMINOTE_PITCH * refInstADPCM_->getSampleRootKeyNumber(); + int p = (baseNoteADPCM_ + (ptSumADPCM_ + + (vibItrADPCM_ ? vibItrADPCM_->data().data : 0) + + detuneADPCM_ + + nsSumADPCM_ + + transposeADPCM_)).getAbsolutePicth(); + int diff = p - Note::SEMINOTE_PITCH * refInstADPCM_->getSampleRootKeyNumber(); writePitchADPCMToRegister(diff, refInstADPCM_->getSampleRootDeltaN()); } else if (refInstKit_) { - int key = clamp(octaveAndNoteToNoteNumber(keyToneADPCM_.octave, keyToneADPCM_.note) - + transposeADPCM_ / calc_pitch::SEMINOTE_PITCH, 0, 95); + int key = utils::clamp(baseNoteADPCM_.getNoteNumber() + + transposeADPCM_ / Note::SEMINOTE_PITCH, 0, Note::NOTE_NUMBER_RANGE - 1); if (refInstKit_->getSampleEnabled(key)) { - int diff = calc_pitch::SEMINOTE_PITCH * refInstKit_->getPitch(key); + int diff = Note::SEMINOTE_PITCH * refInstKit_->getPitch(key); writePitchADPCMToRegister(diff, refInstKit_->getSampleRootDeltaN(key)); hasStartRequestedKit_ = true; } } - needToneSetADPCM_ = false; - needEnvSetADPCM_ = false; + shouldSetToneADPCM_ = false; } void OPNAController::writePitchADPCMToRegister(int pitchDiff, int rtDeltaN) @@ -3884,5 +3821,5 @@ } opna_->setRegister(0x100, 0xa0 | repeatFlag); - opna_->setRegister(0x101, panADPCM_ | 0x02); + opna_->setRegister(0x101, panStateADPCM_ | 0x02); } diff -Nru bambootracker-0.4.5+git20121209/BambooTracker/opna_controller.hpp bambootracker-0.4.6/BambooTracker/opna_controller.hpp --- bambootracker-0.4.5+git20121209/BambooTracker/opna_controller.hpp 2020-12-08 14:05:28.000000000 +0000 +++ bambootracker-0.4.6/BambooTracker/opna_controller.hpp 2021-02-11 14:37:17.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (C) 2018-2020 Rerrah + * Copyright (C) 2018-2021 Rerrah * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation @@ -28,33 +28,14 @@ #include #include #include -#include +#include "song.hpp" #include "instrument.hpp" #include "effect_iterator.hpp" +#include "note.hpp" +#include "echo_buffer.hpp" #include "chip/opna.hpp" -#include "chip/scci/scci.hpp" -#include "chip/c86ctl/c86ctl_wrapper.hpp" #include "enum_hash.hpp" -#include "misc.hpp" - -struct ToneDetail -{ - int octave; - Note note; - int pitch; -}; - -struct SSGToneNoise -{ - bool isTone, isNoise; - int noisePeriod_; -}; - -struct SSGWaveform -{ - SSGWaveformType type; - int data; // Same format with CommandSequenceUnit::data -}; +#include "bamboo_tracker_defs.hpp" class OPNAController { @@ -86,11 +67,9 @@ void getStreamSamples(int16_t* container, size_t nSamples); void getOutputHistory(int16_t* history); - static constexpr int OUTPUT_HISTORY_SIZE = 1024; - // Chip mode void setMode(SongType mode); - SongType getMode() const; + SongType getMode() const noexcept; // Mute void setMuteState(SoundSource src, int chInSrc, bool isMute); @@ -112,31 +91,29 @@ std::vector> registerSetBuf_; + void resetState(); + std::unique_ptr outputHistory_; size_t outputHistoryIndex_; std::unique_ptr outputHistoryReady_; std::mutex outputHistoryReadyMutex_; - - void initChip(); - void fillOutputHistory(const int16_t* outputs, size_t nSamples); - void transferReadyHistory(); - void checkRealToneByArpeggio(int seqPos, const std::unique_ptr& arpIt, - const std::deque& baseTone, ToneDetail& keyTone, bool& needToneSet); - void checkPortamento(const std::unique_ptr& arpIt, int prtm, bool hasKeyOnBefore, - bool isTonePrtm, const std::deque& baseTone, ToneDetail& keyTone, - bool& needToneSet); - void checkRealToneByPitch(int seqPos, const std::unique_ptr& ptIt, - int& sumPitch, bool& needToneSet); + using ArpeggioIterInterface = std::unique_ptr>; + void checkRealToneByArpeggio(const ArpeggioIterInterface& arpItr, + const EchoBuffer& echoBuf, Note& baseNote, bool& shouldSetTone); + void checkPortamento(const ArpeggioIterInterface& arpItr, int prtm, bool hasKeyOnBefore, + bool isTonePrtm, EchoBuffer& echoBuf, Note& baseNote, + bool& shouldSetTone); + void checkRealToneByPitch(const std::unique_ptr::Iterator>& ptItr, + int& sumPitch, bool& shouldSetTone); /*----- FM -----*/ public: // Key on-off - void keyOnFM(int ch, Note note, int octave, int pitch, bool isJam = false); + void keyOnFM(int ch, const Note& note, bool isJam = false); void keyOnFM(int ch, int echoBuf); void keyOffFM(int ch, bool isJam = false); - void updateEchoBufferFM(int ch, int octave, Note note, int pitch); // Set instrument void setInstrumentFM(int ch, std::shared_ptr inst); @@ -149,7 +126,7 @@ // Set volume void setVolumeFM(int ch, int volume); - void setTemporaryVolumeFM(int ch, int volume); + void setOneshotVolumeFM(int ch, int volume); void setMasterVolumeFM(double dB); // Set effect @@ -178,40 +155,50 @@ bool isKeyOnFM(int ch) const; bool isTonePortamentoFM(int ch) const; bool enableFMEnvelopeReset(int ch) const; - ToneDetail getFMTone(int ch) const; + Note getFMLatestNote(int ch) const; private: + // Seperate FM 3ch in FM3chEx mode + struct FMChannel + { + size_t ch, inCh; + bool isKeyOn, hasKeyOnBefore; + EchoBuffer echoBuf; + bool neverSetBaseNote; + Note baseNote; + int baseVol, oneshotVol; + bool isMute; + bool isEnabledEnvReset, hasResetEnv; + bool hasPreSetTickEvent; + bool shouldSetTone; + ArpeggioIterInterface arpItr; + PitchIter ptItr; + int ptSum; + bool isArpEff; + int prtmDepth; + bool isTonePrtm; + std::unique_ptr vibItr; + std::unique_ptr treItr; + int volSld, volSldSum; + int detune, fdetune; + std::unique_ptr nsItr; + int nsSum; + bool hasSetNs; + int transpose; + FMOperatorType opType; + }; + FMChannel fm_[9]; + + // Share FM 3ch in FM3chEx mode std::shared_ptr refInstFM_[6]; std::unique_ptr envFM_[6]; - bool isKeyOnFM_[9], hasKeyOnBeforeFM_[9]; uint8_t fmOpEnables_[6]; - std::deque baseToneFM_[9]; - ToneDetail keyToneFM_[9]; - int sumPitchFM_[9]; - int baseVolFM_[9], tmpVolFM_[9]; /// bit0: right on/off /// bit1: left on/off uint8_t panFM_[6]; - bool isMuteFM_[9]; - bool enableEnvResetFM_[9], hasResetEnvFM_[9]; int lfoFreq_; int lfoStartCntFM_[6]; - bool hasPreSetTickEventFM_[9]; - bool needToneSetFM_[9]; - std::unordered_map> opSeqItFM_[6]; - std::unique_ptr arpItFM_[9]; - std::unique_ptr ptItFM_[9]; - bool isArpEffFM_[9]; - int prtmFM_[9]; - bool isTonePrtmFM_[9]; - std::unique_ptr vibItFM_[9]; - std::unique_ptr treItFM_[9]; - int volSldFM_[9], sumVolSldFM_[9]; - int detuneFM_[9], fdetuneFM_[9]; - std::unique_ptr nsItFM_[9]; - int sumNoteSldFM_[9]; - bool noteSldFMSetFlag_[9]; - int transposeFM_[9]; + std::unordered_map opSeqItFM_[6]; bool isFBCtrlFM_[6], isTLCtrlFM_[6][4], isMLCtrlFM_[6][4], isARCtrlFM_[6][4]; bool isDRCtrlFM_[6][4], isRRCtrlFM_[6][4]; bool isBrightFM_[6][4]; @@ -222,104 +209,40 @@ bool isMuteFM(int ch); uint32_t getFMChannelOffset(int ch, bool forPitch = false) const; - FMOperatorType toChannelOperatorType(int ch) const; - const std::unordered_map> FM_ENV_PARAMS_OP_; - - void updateFMVolume(int ch); - - void writeFMEnvelopeToRegistersFromInstrument(int inch); - void writeFMEnveropeParameterToRegister(int inch, FMEnvelopeParameter param, int value); - - void writeFMLFOAllRegisters(int inch); - void writeFMLFORegister(int inch, FMLFOParameter param); - void checkLFOUsed(); - - void setFrontFMSequences(int ch); - void releaseStartFMSequences(int ch); - void tickEventFM(int ch); - - void checkOperatorSequenceFM(int ch, int type); - void checkVolumeEffectFM(int ch); - inline void checkRealToneFMByArpeggio(int ch, int seqPos) - { - checkRealToneByArpeggio(seqPos, arpItFM_[ch], baseToneFM_[ch], keyToneFM_[ch], needToneSetFM_[ch]); - } + void updateFMVolume(FMChannel& fm); - inline void checkPortamentoFM(int ch) - { - checkPortamento(arpItFM_[ch], prtmFM_[ch], hasKeyOnBeforeFM_[ch], isTonePrtmFM_[ch], baseToneFM_[ch], - keyToneFM_[ch], needToneSetFM_[ch]); - } + void writeFMEnvelopeToRegistersFromInstrument(size_t inch); + void writeFMEnveropeParameterToRegister(size_t inch, FMEnvelopeParameter param, int value); - inline void checkRealToneFMByPitch(int ch, int seqPos) - { - checkRealToneByPitch(seqPos, ptItFM_[ch], sumPitchFM_[ch], needToneSetFM_[ch]); - } + void writeFMLFOAllRegisters(size_t inch); + void writeFMLFORegister(size_t inch, FMLFOParameter param); + void checkLFOUsedByInstrument(); - void writePitchFM(int ch); + void setFrontFMSequences(FMChannel& fm); + void releaseStartFMSequences(FMChannel& fm); + void tickEventFM(FMChannel& fm); - void setInstrumentFMProperties(int ch); + void checkOperatorSequenceFM(FMChannel& fm, int type); + void checkVolumeEffectFM(FMChannel& fm); - static std::vector getOperatorsInLevel(int level, int al); + void checkRealToneFMByArpeggio(FMChannel& fm); + void checkPortamentoFM(FMChannel& fm); + void checkRealToneFMByPitch(FMChannel& fm); - const bool CARRIER_JUDGE_[4][8] /* [operator][algorithm] */ = { - { false, false, false, false, false, false, false, true }, - { false, false, false, false, true, true, true, true }, - { false, false, false, false, false, true, true, true }, - { true, true, true, true, true, true, true, true }, - }; - const uint8_t FM_KEYOFF_MASK_[6] = { 0, 1, 2, 4, 5, 6 }; - const std::unordered_map FM3_KEY_OFF_MASK_ = { { 2, 0xe }, { 6, 0xd }, { 7, 0xb }, { 8, 0x7 } }; - const FMEnvelopeParameter PARAM_TL_[4] = { - FMEnvelopeParameter::TL1, FMEnvelopeParameter::TL2, FMEnvelopeParameter::TL3, FMEnvelopeParameter::TL4 - }; - const FMEnvelopeParameter PARAM_ML_[4] = { - FMEnvelopeParameter::ML1, FMEnvelopeParameter::ML2, FMEnvelopeParameter::ML3, FMEnvelopeParameter::ML4 - }; - const FMEnvelopeParameter PARAM_AR_[4] = { - FMEnvelopeParameter::AR1, FMEnvelopeParameter::AR2, FMEnvelopeParameter::AR3, FMEnvelopeParameter::AR4 - }; - const FMEnvelopeParameter PARAM_DR_[4] = { - FMEnvelopeParameter::DR1, FMEnvelopeParameter::DR2, FMEnvelopeParameter::DR3, FMEnvelopeParameter::DR4 - }; - const FMEnvelopeParameter PARAM_RR_[4] = { - FMEnvelopeParameter::RR1, FMEnvelopeParameter::RR2, FMEnvelopeParameter::RR3, FMEnvelopeParameter::RR4 - }; + void writePitchFM(FMChannel& fm); - inline int toInternalFMChannel(int ch) const - { - if (0 <= ch && ch < 6) return ch; - else if (mode_ == SongType::FM3chExpanded && 6 <= ch && ch < 9) return 2; - else throw std::out_of_range("Out of channel range."); - } + void setInstrumentFMProperties(FMChannel& fm); - inline uint8_t getFMKeyOnOffChannelMask(int ch) const - { - return FM_KEYOFF_MASK_[toInternalFMChannel(ch)]; - } - - inline uint8_t getFM3SlotValidStatus() const - { - return fmOpEnables_[2] & (static_cast(isKeyOnFM_[2]) | (static_cast(isKeyOnFM_[6]) << 1) - | (static_cast(isKeyOnFM_[7]) << 2) | (static_cast(isKeyOnFM_[8]) << 3)); - } - - inline bool isCarrier(int op, int al) { return CARRIER_JUDGE_[op][al]; } - - inline uint8_t calculateTL(int ch, uint8_t data) const - { - int v = (tmpVolFM_[ch] == -1) ? baseVolFM_[ch] : tmpVolFM_[ch]; - return (data > 127 - v) ? 127 : static_cast(data + v); - } + uint8_t getFM3SlotValidStatus() const; + uint8_t calculateTL(int ch, uint8_t data) const; /*----- SSG -----*/ public: // Key on-off - void keyOnSSG(int ch, Note note, int octave, int pitch, bool isJam = false); + void keyOnSSG(int ch, const Note& note, bool isJam = false); void keyOnSSG(int ch, int echoBuf); void keyOffSSG(int ch, bool isJam = false); - void updateEchoBufferSSG(int ch, int octave, Note note, int pitch); // Set instrument void setInstrumentSSG(int ch, std::shared_ptr inst); @@ -327,7 +250,7 @@ // Set volume void setVolumeSSG(int ch, int volume); - void setTemporaryVolumeSSG(int ch, int volume); + void setOneshotVolumeSSG(int ch, int volume); void setMasterVolumeSSG(double dB); // Set effect @@ -350,95 +273,78 @@ // Chip details bool isKeyOnSSG(int ch) const; bool isTonePortamentoSSG(int ch) const; - ToneDetail getSSGTone(int ch) const; + Note getSSGLatestNote(int ch) const; private: - std::shared_ptr refInstSSG_[3]; - bool isKeyOnSSG_[3], hasKeyOnBeforeSSG_[9]; + struct SSGChannel + { + size_t ch; + std::shared_ptr refInst; + bool isKeyOn, hasKeyOnBefore, isInKeyOnProcess_; + EchoBuffer echoBuf; + bool neverSetBaseNote; + Note baseNote; + int baseVol, oneshotVol; + bool isMute; + bool shouldSkip1stTickExec; + bool shouldSetEnv; + bool shouldSetSqMaskFreq; + bool shouldSetHardEnvFreq; + bool shouldUpdateMixState; + bool shouldSetTone; + SSGWaveformIter wfItr; + SSGWaveformUnit wfChState; + SSGEnvelopeIter envItr; + SSGEnvelopeUnit envState; + bool isHardEnv; + SSGToneNoiseIter tnItr; + bool hasRequestedTnEffSet; + ArpeggioIterInterface arpItr; + PitchIter ptItr; + int ptSum; + bool isArpEff; + int prtmDepth; + bool isTonePrtm; + std::unique_ptr vibItr; + std::unique_ptr treItr; + int volSld, volSldSum; + int detune, fdetune; + std::unique_ptr nsItr; + int nsSum; + bool hasSetNs; + int transpose; + } ssg_[3]; + /// Flag "on" is key "off" uint8_t mixerSSG_; - std::deque baseToneSSG_[3]; - ToneDetail keyToneSSG_[3]; - int sumPitchSSG_[3]; - SSGToneNoise tnSSG_[3]; - int baseVolSSG_[3], tmpVolSSG_[3]; - bool isBuzzEffSSG_[3]; - bool isHardEnvSSG_[3]; - bool isMuteSSG_[3]; - bool hasPreSetTickEventSSG_[3]; - bool needEnvSetSSG_[3]; - bool setHardEnvIfNecessary_[3]; - bool needMixSetSSG_[3]; - bool needToneSetSSG_[3]; - bool needSqMaskFreqSetSSG_[3]; - std::unique_ptr wfItSSG_[3]; - SSGWaveform wfSSG_[3]; - std::unique_ptr envItSSG_[3]; - CommandSequenceUnit envSSG_[3]; - std::unique_ptr tnItSSG_[3]; - std::unique_ptr arpItSSG_[3]; - std::unique_ptr ptItSSG_[3]; - bool isArpEffSSG_[3]; - int prtmSSG_[3]; - bool isTonePrtmSSG_[3]; - std::unique_ptr vibItSSG_[3]; - std::unique_ptr treItSSG_[3]; - int volSldSSG_[3], sumVolSldSSG_[3]; - int detuneSSG_[3], fdetuneSSG_[3]; - std::unique_ptr nsItSSG_[3]; - int sumNoteSldSSG_[3]; - bool noteSldSSGSetFlag_; - int transposeSSG_[3]; - int toneNoiseMixSSG_[3]; - int noisePitchSSG_; + uint8_t noisePeriodSSG_; int hardEnvPeriodHighSSG_, hardEnvPeriodLowSSG_; - const int AUTO_ENV_SHAPE_TYPE_[15] = { 17, 17, 17, 21, 21, 21, 21, 16, 17, 18, 19, 20, 21, 22, 23 }; void initSSG(); void setMuteSSGState(int ch, bool isMuteFM); bool isMuteSSG(int ch); - void setFrontSSGSequences(int ch); - void releaseStartSSGSequences(int ch); - void tickEventSSG(int ch); - - void writeWaveformSSGToRegister(int ch, int seqPos); - void writeSquareWaveform(int ch); - - void writeToneNoiseSSGToRegister(int ch, int seqPos); - void writeToneNoiseSSGToRegisterNoReference(int ch); - - void writeEnvelopeSSGToRegister(int ch, int seqPos); - - inline uint8_t SSGToneFlag(int ch) { return (1 << ch); } - inline uint8_t SSGNoiseFlag(int ch) { return (8 << ch); } - - inline void checkRealToneSSGByArpeggio(int ch, int seqPos) - { - checkRealToneByArpeggio(seqPos, arpItSSG_[ch], baseToneSSG_[ch], keyToneSSG_[ch], needToneSetSSG_[ch]); - } - - inline void checkPortamentoSSG(int ch) - { - checkPortamento(arpItSSG_[ch], prtmSSG_[ch], hasKeyOnBeforeSSG_[ch], isTonePrtmSSG_[ch], baseToneSSG_[ch], - keyToneSSG_[ch], needToneSetSSG_[ch]); - } - - inline void checkRealToneSSGByPitch(int ch, int seqPos) - { - checkRealToneByPitch(seqPos, ptItSSG_[ch], sumPitchSSG_[ch], needToneSetSSG_[ch]); - } - - void writePitchSSG(int ch); - void writeAutoEnvelopePitchSSG(int ch, double tonePitch); - void writeSquareMaskPitchSSG(int ch, double tonePitch, bool isTriangle); - - void setRealVolumeSSG(int ch); - - inline uint8_t judgeSSEGRegisterValue(int v) - { - return (v == -1) ? 0 : (0x08 + static_cast(v)); - } + void setFrontSSGSequences(SSGChannel& ssg); + void releaseStartSSGSequences(SSGChannel& ssg); + void tickEventSSG(SSGChannel& ssg); + + void writeWaveformSSGToRegister(SSGChannel& ssg); + void writeSquareWaveform(SSGChannel& ssg); + + void writeEnvelopeSSGToRegister(SSGChannel& ssg); + void setRealVolumeSSG(SSGChannel& ssg); + + void writeMixerSSGToRegisterByEffect(SSGChannel& ssg); + void writeMixerSSGToRegisterBySequence(SSGChannel& ssg); + void writeMixerSSGToRegisterByNoReference(SSGChannel& ssg); + + void checkRealToneSSGByArpeggio(SSGChannel& ssg); + void checkPortamentoSSG(SSGChannel& ssg); + void checkRealToneSSGByPitch(SSGChannel& ssg); + + void writePitchSSG(SSGChannel& ssg); + void writeAutoEnvelopePitchSSG(SSGChannel& ssg, double tonePitch); + void writeSquareMaskPitchSSG(SSGChannel& ssg, double tonePitch, bool isTriangle); /*----- Rhythm -----*/ public: @@ -448,19 +354,23 @@ // Set volume void setVolumeRhythm(int ch, int volume); + void setOneshotVolumeRhythm(int ch, int volume); void setMasterVolumeRhythm(int volume); - void setTemporaryVolumeRhythm(int ch, int volume); // Set effect void setPanRhythm(int ch, int value); private: - uint8_t keyOnFlagRhythm_, keyOffFlagRhythm_; - int volRhythm_[6], mVolRhythm_, tmpVolRhythm_[6]; - /// bit0: right on/off - /// bit1: left on/off - uint8_t panRhythm_[6]; - bool isMuteRhythm_[6]; + struct RhythmChannel + { + int baseVol, oneshotVol; + /// bit0: right on/off + /// bit1: left on/off + uint8_t panState; + bool isMute; + } rhythm_[6]; + uint8_t keyOnRequestFlagsRhythm_, keyOffRequestFlagsRhythm_; + int masterVolRhythm_; void initRhythm(); @@ -472,10 +382,9 @@ /*----- ADPCM/Drumkit -----*/ public: // Key on-off - void keyOnADPCM(Note note, int octave, int pitch, bool isJam = false); + void keyOnADPCM(const Note& note, bool isJam = false); void keyOnADPCM(int echoBuf); void keyOffADPCM(bool isJam = false); - void updateEchoBufferADPCM(int octave, Note note, int pitch); // Set instrument void setInstrumentADPCM(std::shared_ptr inst); @@ -483,12 +392,12 @@ void setInstrumentDrumkit(std::shared_ptr inst); void updateInstrumentDrumkit(int instNum, int key); void clearSamplesADPCM(); - /// return: [0]: start address, [1]: stop address - std::vector storeSampleADPCM(std::vector sample); + /// [Return] true if sample assignment is success + bool storeSampleADPCM(const std::vector& sample, size_t& startAddr, size_t& stopAddr); // Set volume void setVolumeADPCM(int volume); - void setTemporaryVolumeADPCM(int volume); + void setOneshotVolumeADPCM(int volume); // Set effect void setPanADPCM(int value); @@ -508,38 +417,38 @@ // Chip details bool isKeyOnADPCM() const; bool isTonePortamentoADPCM() const; - ToneDetail getADPCMTone() const; + Note getADPCMLatestNote() const; size_t getADPCMStoredSize() const; private: std::shared_ptr refInstADPCM_; std::shared_ptr refInstKit_; bool isKeyOnADPCM_, hasKeyOnBeforeADPCM_; - std::deque baseToneADPCM_; - ToneDetail keyToneADPCM_; - int sumPitchADPCM_; - int baseVolADPCM_, tmpVolADPCM_; - uint8_t panADPCM_; + EchoBuffer echoBufADPCM_; + bool neverSetBaseNoteADPCM_; + Note baseNoteADPCM_; + int baseVolADPCM_, oneshotVolADPCM_; + uint8_t panStateADPCM_; bool isMuteADPCM_; - bool hasPreSetTickEventADPCM_; - bool needEnvSetADPCM_; - bool needToneSetADPCM_; + bool shouldSkip1stTickExecADPCM_; // Use to execute key on/off process in jamming + bool shouldWriteEnvADPCM_; + bool shouldSetToneADPCM_; size_t startAddrADPCM_, stopAddrADPCM_; // By 32 bytes size_t storePointADPCM_; // Move by 32 bytes - std::unique_ptr envItADPCM_; - std::unique_ptr tnItADPCM_; - std::unique_ptr arpItADPCM_; - std::unique_ptr ptItADPCM_; - bool isArpEffADPCM_; - int prtmADPCM_; - bool isTonePrtmADPCM_; - std::unique_ptr vibItADPCM_; - std::unique_ptr treItADPCM_; - int volSldADPCM_, sumVolSldADPCM_; + ADPCMEnvelopeIter envItrADPCM_; + ArpeggioIterInterface arpItrADPCM_; + PitchIter ptItrADPCM_; + int ptSumADPCM_; + bool hasArpEffADPCM_; + int prtmDepthADPCM_; + bool hasTonePrtmADPCM_; + std::unique_ptr vibItrADPCM_; + std::unique_ptr treItrADPCM_; + int volSldADPCM_, volSldSumADPCM_; int detuneADPCM_, fdetuneADPCM_; std::unique_ptr nsItADPCM_; - int sumNoteSldADPCM_; - bool noteSldADPCMSetFlag_; + int nsSumADPCM_; + bool hasSetNsADPCM_; int transposeADPCM_; bool hasStartRequestedKit_; @@ -552,23 +461,11 @@ void releaseStartADPCMSequences(); void tickEventADPCM(); - void writeEnvelopeADPCMToRegister(int seqPos); - - inline void checkRealToneADPCMByArpeggio(int seqPos) - { - checkRealToneByArpeggio(seqPos, arpItADPCM_, baseToneADPCM_, keyToneADPCM_, needToneSetADPCM_); - } - - inline void checkPortamentoADPCM() - { - checkPortamento(arpItADPCM_, prtmADPCM_, hasKeyOnBeforeADPCM_, isTonePrtmADPCM_, baseToneADPCM_, - keyToneADPCM_, needToneSetADPCM_); - } + void writeEnvelopeADPCMToRegister(); - inline void checkRealToneADPCMByPitch(int seqPos) - { - checkRealToneByPitch(seqPos, ptItADPCM_, sumPitchADPCM_, needToneSetADPCM_); - } + void checkRealToneADPCMByArpeggio(); + void checkPortamentoADPCM(); + void checkRealToneADPCMByPitch(); void writePitchADPCM(); void writePitchADPCMToRegister(int pitchDiff, int rtDeltaN); @@ -577,3 +474,67 @@ void triggerSamplePlayADPCM(size_t startAddress, size_t stopAddress, bool repeatable); }; + +//----------------------------------------------------------------------------- + +inline SongType OPNAController::getMode() const noexcept +{ + return mode_; +} + +/*----- FM -----*/ +inline void OPNAController::checkRealToneFMByArpeggio(FMChannel& fm) +{ + checkRealToneByArpeggio(fm.arpItr, fm.echoBuf, fm.baseNote, fm.shouldSetTone); +} + +inline void OPNAController::checkPortamentoFM(FMChannel& fm) +{ + checkPortamento(fm.arpItr, fm.prtmDepth, fm.hasKeyOnBefore, fm.isTonePrtm, fm.echoBuf, + fm.baseNote, fm.shouldSetTone); +} + +inline void OPNAController::checkRealToneFMByPitch(FMChannel& fm) +{ + checkRealToneByPitch(fm.ptItr, fm.ptSum, fm.shouldSetTone); +} + +inline uint8_t OPNAController::getFM3SlotValidStatus() const +{ + return fmOpEnables_[2] & (static_cast(fm_[2].isKeyOn) | (static_cast(fm_[6].isKeyOn) << 1) + | (static_cast(fm_[7].isKeyOn) << 2) | (static_cast(fm_[8].isKeyOn) << 3)); +} + +/*----- SSG -----*/ +inline void OPNAController::checkRealToneSSGByArpeggio(OPNAController::SSGChannel& ssg) +{ + checkRealToneByArpeggio(ssg.arpItr, ssg.echoBuf, ssg.baseNote, ssg.shouldSetTone); +} + +inline void OPNAController::checkPortamentoSSG(OPNAController::SSGChannel& ssg) +{ + checkPortamento(ssg.arpItr, ssg.prtmDepth, ssg.hasKeyOnBefore, ssg.isTonePrtm, ssg.echoBuf, + ssg.baseNote, ssg.shouldSetTone); +} + +inline void OPNAController::checkRealToneSSGByPitch(OPNAController::SSGChannel& ssg) +{ + checkRealToneByPitch(ssg.ptItr, ssg.ptSum, ssg.shouldSetTone); +} + +/*----- ADPCM/Drumkit -----*/ +inline void OPNAController::checkRealToneADPCMByArpeggio() +{ + checkRealToneByArpeggio(arpItrADPCM_, echoBufADPCM_, baseNoteADPCM_, shouldSetToneADPCM_); +} + +inline void OPNAController::checkPortamentoADPCM() +{ + checkPortamento(arpItrADPCM_, prtmDepthADPCM_, hasKeyOnBeforeADPCM_, hasTonePrtmADPCM_, echoBufADPCM_, + baseNoteADPCM_, shouldSetToneADPCM_); +} + +inline void OPNAController::checkRealToneADPCMByPitch() +{ + checkRealToneByPitch(ptItrADPCM_, ptSumADPCM_, shouldSetToneADPCM_); +} diff -Nru bambootracker-0.4.5+git20121209/BambooTracker/playback.cpp bambootracker-0.4.6/BambooTracker/playback.cpp --- bambootracker-0.4.5+git20121209/BambooTracker/playback.cpp 2020-12-08 14:05:28.000000000 +0000 +++ bambootracker-0.4.6/BambooTracker/playback.cpp 2021-02-11 14:37:17.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (C) 2019-2020 Rerrah + * Copyright (C) 2019-2021 Rerrah * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation @@ -28,7 +28,27 @@ #include "opna_controller.hpp" #include "instruments_manager.hpp" #include "tick_counter.hpp" -#include "misc.hpp" +#include "note.hpp" +#include "utils.hpp" + +EffectMemory::EffectMemory() +{ + mem_.reserve(Step::N_EFFECT); +} + +void EffectMemory::enqueue(const Effect& eff) +{ + auto itr = utils::findIf(mem_, [type = eff.type](const Effect& a) { return a.type == type; }); + if (itr != mem_.end()) mem_.erase(itr); + mem_.push_back(eff); +} + +void EffectMemory::clear() +{ + mem_.clear(); +} + +//----------------------------------------------- namespace { @@ -36,6 +56,7 @@ { enum : uint8_t { + Clear = 0, // Read state Playing = 1 << 0, ReadFirstStep = 1 << 1, @@ -55,8 +76,8 @@ tickCounter_(tickCounter), mod_(mod), curSongNum_(0), - playOrderNum_(-1), - playStepNum_(-1), + playingPos_(Position::INVALID, Position::INVALID), + nextReadPos_(Position::INVALID, Position::INVALID), isFindNextStep_(false), isRetrieveChannel_(isRetrieveChannel) { @@ -75,10 +96,10 @@ /* opna mode is changed in BambooTracker class */ - size_t fmch = getFMChannelCount(songStyle_.type); + size_t fmch = Song::getFMChannelCount(songStyle_.type); isNoteDelay_[SoundSource::FM] = std::vector(fmch); - keyOnBasedEffs_[SoundSource::FM] = EffectMemorySource(fmch); - stepBeginBasedEffs_[SoundSource::FM] = EffectMemorySource(fmch); + effOnKeyOnMem_[SoundSource::FM] = std::vector(fmch); + effOnStepBeginMem_[SoundSource::FM] = std::vector(fmch); directRegisterSets_[SoundSource::FM] = DirectRegisterSetSource(fmch); ntDlyCntFM_ = std::vector(fmch); ntCutDlyCntFM_ = std::vector(fmch); @@ -88,8 +109,8 @@ tposeDlyValueFM_ = std::vector(fmch); isNoteDelay_[SoundSource::SSG] = std::vector(3); - keyOnBasedEffs_[SoundSource::SSG] = EffectMemorySource(3); - stepBeginBasedEffs_[SoundSource::SSG] = EffectMemorySource(3); + effOnKeyOnMem_[SoundSource::SSG] = std::vector(3); + effOnStepBeginMem_[SoundSource::SSG] = std::vector(3); directRegisterSets_[SoundSource::SSG] = DirectRegisterSetSource(3); ntDlyCntSSG_ = std::vector(3); ntCutDlyCntSSG_ = std::vector(3); @@ -99,8 +120,8 @@ tposeDlyValueSSG_ = std::vector(3); isNoteDelay_[SoundSource::RHYTHM] = std::vector(6); - keyOnBasedEffs_[SoundSource::RHYTHM] = EffectMemorySource(6); - stepBeginBasedEffs_[SoundSource::RHYTHM] = EffectMemorySource(6); + effOnKeyOnMem_[SoundSource::RHYTHM] = std::vector(6); + effOnStepBeginMem_[SoundSource::RHYTHM] = std::vector(6); directRegisterSets_[SoundSource::RHYTHM] = DirectRegisterSetSource(6); ntDlyCntRhythm_ = std::vector(6); ntCutDlyCntRhythm_ = std::vector(6); @@ -108,8 +129,8 @@ volDlyValueRhythm_ = std::vector(6, -1); isNoteDelay_[SoundSource::ADPCM] = std::vector(1); - keyOnBasedEffs_[SoundSource::ADPCM] = EffectMemorySource(1); - stepBeginBasedEffs_[SoundSource::ADPCM] = EffectMemorySource(1); + effOnKeyOnMem_[SoundSource::ADPCM] = std::vector(1); + effOnStepBeginMem_[SoundSource::ADPCM] = std::vector(1); directRegisterSets_[SoundSource::ADPCM] = DirectRegisterSetSource(1); ntDlyCntADPCM_ = 0; ntCutDlyCntADPCM_ = 0; @@ -124,8 +145,7 @@ startPlay(); playStateFlags_ = PlayStateFlag::Playing; - playStepNum_ = 0; - playOrderNum_ = order; + playingPos_.set(order, 0); findNextStep(); if (isRetrieveChannel_) retrieveChannelStates(); } @@ -136,8 +156,7 @@ startPlay(); playStateFlags_ = PlayStateFlag::Playing; - playOrderNum_ = 0; - playStepNum_ = 0; + playingPos_.set(0, 0); findNextStep(); } @@ -147,8 +166,7 @@ startPlay(); playStateFlags_ = PlayStateFlag::Playing | PlayStateFlag::LoopPattern; - playStepNum_ = 0; - playOrderNum_ = order; + playingPos_.set(order, 0); findNextStep(); if (isRetrieveChannel_) retrieveChannelStates(); } @@ -159,8 +177,7 @@ startPlay(); playStateFlags_ = PlayStateFlag::Playing; - playOrderNum_ = order; - playStepNum_ = step; + playingPos_.set(order, step); findNextStep(); if (isRetrieveChannel_) retrieveChannelStates(); } @@ -176,7 +193,7 @@ Song& song = mod_.lock()->getSong(curSongNum_); tickCounter_.lock()->setTempo(song.getTempo()); tickCounter_.lock()->setSpeed(song.getSpeed()); - tickCounter_.lock()->setGroove(mod_.lock()->getGroove(song.getGroove()).getSequence()); + tickCounter_.lock()->setGroove(mod_.lock()->getGroove(song.getGroove())); tickCounter_.lock()->setGrooveState(song.isUsedTempo() ? GrooveState::Invalid : GrooveState::ValidByGlobal); } @@ -188,8 +205,7 @@ clearDelayBeyondStepCounts(); playStateFlags_ = PlayStateFlag::PlayStep; - playOrderNum_ = order; - playStepNum_ = step; + playingPos_.set(order, step); if (!isPlaying && isRetrieveChannel_) retrieveChannelStates(); } @@ -201,7 +217,7 @@ Song& song = mod_.lock()->getSong(curSongNum_); tickCounter_.lock()->setTempo(song.getTempo()); tickCounter_.lock()->setSpeed(song.getSpeed()); - tickCounter_.lock()->setGroove(mod_.lock()->getGroove(song.getGroove()).getSequence()); + tickCounter_.lock()->setGroove(mod_.lock()->getGroove(song.getGroove())); tickCounter_.lock()->setGrooveState(song.isUsedTempo() ? GrooveState::Invalid : GrooveState::ValidByGlobal); tickCounter_.lock()->resetCount(); @@ -224,9 +240,8 @@ opnaCtrl_->reset(); tickCounter_.lock()->setPlayState(false); - playStateFlags_ = 0; - playOrderNum_ = -1; - playStepNum_ = -1; + playStateFlags_ = PlayStateFlag::Clear; + playingPos_.invalidate(); } bool PlaybackManager::isPlaySong() const noexcept @@ -241,12 +256,12 @@ int PlaybackManager::getPlayingOrderNumber() const noexcept { - return playOrderNum_; + return playingPos_.order; } int PlaybackManager::getPlayingStepNumber() const noexcept { - return playStepNum_; + return playingPos_.step; } /********** Stream events **********/ @@ -286,13 +301,12 @@ return false; } else { - if (nextReadOrder_ == -1) { + if (!nextReadPos_.isValid()) { isFindNextStep_ = false; return false; } else { - playOrderNum_ = nextReadOrder_; - playStepNum_ = nextReadStep_; + playingPos_ = nextReadPos_; } } } @@ -306,24 +320,23 @@ void PlaybackManager::findNextStep() { // Init - nextReadOrder_ = playOrderNum_; - nextReadStep_ = playStepNum_; + nextReadPos_ = playingPos_; // Search - int ptnSize = static_cast(getPatternSizeFromOrderNumber(curSongNum_, nextReadOrder_)); - if (!ptnSize || nextReadStep_ >= ptnSize - 1) { + int ptnSize = static_cast(getPatternSizeFromOrderNumber(curSongNum_, nextReadPos_.order)); + if (!ptnSize || nextReadPos_.step >= ptnSize - 1) { if (!(playStateFlags_ & PlayStateFlag::LoopPattern)) { // Loop pattern - if (nextReadOrder_ >= static_cast(getOrderSize(curSongNum_)) - 1) { - nextReadOrder_ = 0; + if (nextReadPos_.order >= static_cast(getOrderSize(curSongNum_)) - 1) { + nextReadPos_.order = 0; } else { - ++nextReadOrder_; + ++nextReadPos_.order; } } - nextReadStep_ = 0; + nextReadPos_.step = 0; } else { - ++nextReadStep_; + ++nextReadPos_.step; } isFindNextStep_ = true; @@ -333,24 +346,19 @@ { auto& song = mod_.lock()->getSong(curSongNum_); int orderSize = static_cast(song.getOrderSize()); - if (playOrderNum_ >= orderSize) { - playOrderNum_ = 0; - playStepNum_ = 0; - nextReadOrder_ = 0; - nextReadStep_ = 0; - } - else if (playStepNum_ >= static_cast(song.getPatternSizeFromOrderNumber(playOrderNum_))) { - if (playOrderNum_ == orderSize - 1) { - playOrderNum_ = 0; - playStepNum_ = 0; - nextReadOrder_ = 0; - nextReadStep_ = 0; + if (playingPos_.order >= orderSize) { + playingPos_.set(0, 0); + nextReadPos_ = playingPos_; + } + else if (playingPos_.step >= static_cast(song.getPatternSizeFromOrderNumber(playingPos_.order))) { + if (playingPos_.order == orderSize - 1) { + playingPos_.set(0, 0); + nextReadPos_ = playingPos_; } else { - ++playOrderNum_; - playStepNum_ = 0; - nextReadOrder_ = playOrderNum_; - nextReadStep_ = 0; + ++playingPos_.order; + playingPos_.step = 0; + nextReadPos_ = playingPos_; } } } @@ -366,21 +374,21 @@ // Store effects from the step to map for (auto& attrib : songStyle_.trackAttribs) { auto& step = song.getTrack(attrib.number) - .getPatternFromOrderNumber(playOrderNum_).getStep(playStepNum_); + .getPatternFromOrderNumber(playingPos_.order).getStep(playingPos_.step); size_t uch = static_cast(attrib.channelInSource); - keyOnBasedEffs_[attrib.source].at(uch).clear(); + effOnKeyOnMem_[attrib.source].at(uch).clear(); directRegisterSets_[attrib.source].at(uch).clear(); - bool (PlaybackManager::*storeEffectToMap)(int, const Effect&) = nullptr; - switch (attrib.source) { - case SoundSource::FM: storeEffectToMap = &PlaybackManager::storeEffectToMapFM; break; - case SoundSource::SSG: storeEffectToMap = &PlaybackManager::storeEffectToMapSSG; break; - case SoundSource::RHYTHM: storeEffectToMap = &PlaybackManager::storeEffectToMapRhythm; break; - case SoundSource::ADPCM: storeEffectToMap = &PlaybackManager::storeEffectToMapADPCM; break; - } + using storeFunc = bool (PlaybackManager::*)(int, const Effect&); + static const std::unordered_map storeEffectToMap = { + { SoundSource::FM, &PlaybackManager::storeEffectToMapFM }, + { SoundSource::SSG, &PlaybackManager::storeEffectToMapSSG }, + { SoundSource::RHYTHM, &PlaybackManager::storeEffectToMapRhythm }, + { SoundSource::ADPCM, &PlaybackManager::storeEffectToMapADPCM } + }; bool isDelay = false; - for (int i = 0; i < 4; ++i) { - Effect&& eff = Effect::makeEffectData(attrib.source, step.getEffectID(i), step.getEffectValue(i)); - isDelay |= (this->*storeEffectToMap)(attrib.channelInSource, std::move(eff)); + for (int i = 0; i < Step::N_EFFECT; ++i) { + Effect&& eff = effect_utils::validateEffect(attrib.source, step.getEffect(i)); + isDelay |= (this->*storeEffectToMap.at(attrib.source))(attrib.channelInSource, eff); } isNoteDelay_[attrib.source].at(uch) = isDelay; } @@ -389,7 +397,7 @@ bool isNextSet = executeStoredEffectsGlobal(); for (auto& attrib : songStyle_.trackAttribs) { auto& step = song.getTrack(attrib.number) - .getPatternFromOrderNumber(playOrderNum_).getStep(playStepNum_); + .getPatternFromOrderNumber(playingPos_.order).getStep(playingPos_.step); switch (attrib.source) { case SoundSource::FM: if (isNoteDelay_[SoundSource::FM].at(attrib.channelInSource)) { @@ -439,19 +447,18 @@ isFindNextStep_ = isNextSet; } -void PlaybackManager::executeFMStepEvents(Step& step, int ch, bool calledByNoteDelay) +void PlaybackManager::executeFMStepEvents(const Step& step, int ch, bool calledByNoteDelay) { - int noteNum = step.getNoteNumber(); - if (!calledByNoteDelay && noteNum != -1) clearFMDelayBeyondStepCounts(ch); // Except no key event + if (!calledByNoteDelay && !step.isEmptyNote()) clearFMDelayBeyondStepCounts(ch); // Except no key event // Set volume int vol = step.getVolume(); - if (0 <= vol && vol < 0x80) { + if (step.hasVolume() && vol < bt_defs::NSTEP_FM_VOLUME) { opnaCtrl_->setVolumeFM(ch, vol); } // Set instrument - if (step.getInstrumentNumber() != -1) { + if (step.hasInstrument()) { if (auto inst = std::dynamic_pointer_cast( instMan_.lock()->getInstrumentSharedPtr(step.getInstrumentNumber()))) opnaCtrl_->setInstrumentFM(ch, inst); @@ -464,50 +471,47 @@ executeStoredEffectsFM(ch); // Set key + int noteNum = step.getNoteNumber(); switch (noteNum) { - case -1: // None + case Step::NOTE_NONE: if (!calledByNoteDelay) { // When this is called by note delay, tick event will be updated in readTick checkFMDelayEventsInTick(step, ch); opnaCtrl_->tickEvent(SoundSource::FM, ch); } break; - case -2: // Key off + case Step::NOTE_KEY_OFF: opnaCtrl_->keyOffFM(ch); break; - case -3: // Echo 0 + case Step::NOTE_ECHO0: opnaCtrl_->keyOnFM(ch, 0); break; - case -4: // Echo 1 + case Step::NOTE_ECHO1: opnaCtrl_->keyOnFM(ch, 1); break; - case -5: // Echo 2 + case Step::NOTE_ECHO2: opnaCtrl_->keyOnFM(ch, 2); break; - case -6: // Echo 3 + case Step::NOTE_ECHO3: opnaCtrl_->keyOnFM(ch, 3); break; default: // Key on - { - std::pair octNote = noteNumberToOctaveAndNote(step.getNoteNumber()); - opnaCtrl_->keyOnFM(ch, octNote.second, octNote.first, 0); + opnaCtrl_->keyOnFM(ch, Note(noteNum)); break; } - } } -void PlaybackManager::executeSSGStepEvents(Step& step, int ch, bool calledByNoteDelay) +void PlaybackManager::executeSSGStepEvents(const Step& step, int ch, bool calledByNoteDelay) { - int noteNum = step.getNoteNumber(); - if (!calledByNoteDelay && noteNum != -1) clearSSGDelayBeyondStepCounts(ch); // Except no key event + if (!calledByNoteDelay && !step.isEmptyNote()) clearSSGDelayBeyondStepCounts(ch); // Except no key event // Set volume int vol = step.getVolume(); - if (0 <= vol && vol < 0x10) { + if (step.hasVolume() && vol < bt_defs::NSTEP_SSG_VOLUME) { opnaCtrl_->setVolumeSSG(ch, vol); } // Set instrument - if (step.getInstrumentNumber() != -1) { + if (step.hasInstrument()) { if (auto inst = std::dynamic_pointer_cast( instMan_.lock()->getInstrumentSharedPtr(step.getInstrumentNumber()))) opnaCtrl_->setInstrumentSSG(ch, inst); @@ -517,45 +521,42 @@ executeStoredEffectsSSG(ch); // Set key + int noteNum = step.getNoteNumber(); switch (noteNum) { - case -1: // None + case Step::NOTE_NONE: if (!calledByNoteDelay) { // When this is called by note delay, tick event will be updated in readTick checkSSGDelayEventsInTick(step, ch); opnaCtrl_->tickEvent(SoundSource::SSG, ch); } break; - case -2: // Key off + case Step::NOTE_KEY_OFF: opnaCtrl_->keyOffSSG(ch); break; - case -3: // Echo 0 + case Step::NOTE_ECHO0: opnaCtrl_->keyOnSSG(ch, 0); break; - case -4: // Echo 1 + case Step::NOTE_ECHO1: opnaCtrl_->keyOnSSG(ch, 1); break; - case -5: // Echo 2 + case Step::NOTE_ECHO2: opnaCtrl_->keyOnSSG(ch, 2); break; - case -6: // Echo 3 + case Step::NOTE_ECHO3: opnaCtrl_->keyOnSSG(ch, 3); break; default: // Key on - { - std::pair octNote = noteNumberToOctaveAndNote(step.getNoteNumber()); - opnaCtrl_->keyOnSSG(ch, octNote.second, octNote.first, 0); + opnaCtrl_->keyOnSSG(ch, Note(noteNum)); break; } - } } -void PlaybackManager::executeRhythmStepEvents(Step& step, int ch, bool calledByNoteDelay) +void PlaybackManager::executeRhythmStepEvents(const Step& step, int ch, bool calledByNoteDelay) { - int noteNum = step.getNoteNumber(); - if (!calledByNoteDelay && noteNum != -1) clearRhythmDelayBeyondStepCounts(ch); // Except no key event + if (!calledByNoteDelay && !step.isEmptyNote()) clearRhythmDelayBeyondStepCounts(ch); // Except no key event // Set volume int vol = step.getVolume(); - if (0 <= vol && vol < 0x20) { + if (step.hasVolume() && vol < bt_defs::NSTEP_RHYTHM_VOLUME) { opnaCtrl_->setVolumeRhythm(ch, vol); } @@ -563,14 +564,14 @@ executeStoredEffectsRhythm(ch); // Set key - switch (noteNum) { - case -1: // None + switch (step.getNoteNumber()) { + case Step::NOTE_NONE: if (!calledByNoteDelay) { // When this is called by note delay, tick event will be updated in readTick checkRhythmDelayEventsInTick(step, ch); opnaCtrl_->tickEvent(SoundSource::RHYTHM, ch); } break; - case -2: // Key off + case Step::NOTE_KEY_OFF: opnaCtrl_->setKeyOffFlagRhythm(ch); break; default: // Key on & Echo @@ -579,19 +580,18 @@ } } -void PlaybackManager::executeADPCMStepEvents(Step& step, bool calledByNoteDelay) +void PlaybackManager::executeADPCMStepEvents(const Step& step, bool calledByNoteDelay) { - int noteNum = step.getNoteNumber(); - if (!calledByNoteDelay && noteNum != -1) clearADPCMDelayBeyondStepCounts(); // Except no key event + if (!calledByNoteDelay && !step.isEmptyNote()) clearADPCMDelayBeyondStepCounts(); // Except no key event // Set volume int vol = step.getVolume(); - if (0 <= vol && vol < 0x100) { + if (step.hasVolume() && vol < bt_defs::NSTEP_ADPCM_VOLUME) { opnaCtrl_->setVolumeADPCM(vol); } // Set instrument - if (step.getInstrumentNumber() != -1) { + if (step.hasInstrument()) { auto inst = instMan_.lock()->getInstrumentSharedPtr(step.getInstrumentNumber()); if (inst->getType() == InstrumentType::ADPCM) opnaCtrl_->setInstrumentADPCM(std::dynamic_pointer_cast(inst)); @@ -603,35 +603,33 @@ executeStoredEffectsADPCM(); // Set key + int noteNum = step.getNoteNumber(); switch (noteNum) { - case -1: // None + case Step::NOTE_NONE: if (!calledByNoteDelay) { // When this is called by note delay, tick event will be updated in readTick checkADPCMDelayEventsInTick(step); opnaCtrl_->tickEvent(SoundSource::ADPCM, 0); } break; - case -2: // Key off + case Step::NOTE_KEY_OFF: opnaCtrl_->keyOffADPCM(); break; - case -3: // Echo 0 + case Step::NOTE_ECHO0: opnaCtrl_->keyOnADPCM(0); break; - case -4: // Echo 1 + case Step::NOTE_ECHO1: opnaCtrl_->keyOnADPCM(1); break; - case -5: // Echo 2 + case Step::NOTE_ECHO2: opnaCtrl_->keyOnADPCM(2); break; - case -6: // Echo 3 + case Step::NOTE_ECHO3: opnaCtrl_->keyOnADPCM(3); break; default: // Key on - { - std::pair octNote = noteNumberToOctaveAndNote(step.getNoteNumber()); - opnaCtrl_->keyOnADPCM(octNote.second, octNote.first, 0); + opnaCtrl_->keyOnADPCM(Note(noteNum)); break; } - } } bool PlaybackManager::executeStoredEffectsGlobal() @@ -639,11 +637,11 @@ bool changedNextPos = false; // Read step end based effects - for (const auto& eff : stepEndBasedEffsGlobal_) { - switch (eff.first) { + for (const Effect& eff : posChangeEffMem_) { + switch (eff.type) { case EffectType::PositionJump: if (!(playStateFlags_ & PlayStateFlag::LoopPattern)) { // Skip when loop pattern - changedNextPos |= effPositionJump(eff.second); + changedNextPos |= effPositionJump(eff.value); } break; case EffectType::SongEnd: @@ -654,32 +652,31 @@ break; case EffectType::PatternBreak: if (!(playStateFlags_ & PlayStateFlag::LoopPattern)) { // Skip when loop pattern - changedNextPos |= effPatternBreak(eff.second); + changedNextPos |= effPatternBreak(eff.value); } break; default: break; } } - stepEndBasedEffsGlobal_.clear(); + posChangeEffMem_.clear(); // Read step beginning based effects - auto&& it = stepBeginBasedEffsGlobal_.find(EffectType::SpeedTempoChange); - if (it != stepBeginBasedEffsGlobal_.end()) { - if (it->second < 0x20) effSpeedChange(it->second); - else effTempoChange(it->second); - } - for (const auto& eff : stepBeginBasedEffsGlobal_) { - switch (eff.first) { + for (const Effect& eff : playbackSpeedEffMem_) { + switch (eff.type) { + case EffectType::SpeedTempoChange: + if (eff.value < 0x20) effSpeedChange(eff.value); + else effTempoChange(eff.value); + break; case EffectType::Groove: - if (eff.second < static_cast(mod_.lock()->getGrooveCount())) - effGrooveChange(eff.second); + if (eff.value < static_cast(mod_.lock()->getGrooveCount())) + effGrooveChange(eff.value); break; default: break; } } - stepBeginBasedEffsGlobal_.clear(); + playbackSpeedEffMem_.clear(); return changedNextPos; } @@ -709,22 +706,22 @@ case EffectType::DRControl: case EffectType::RRControl: case EffectType::Brightness: - keyOnBasedEffs_[SoundSource::FM].at(static_cast(ch))[eff.type] = eff.value; + effOnKeyOnMem_[SoundSource::FM].at(static_cast(ch)).enqueue(eff); return false; case EffectType::SpeedTempoChange: case EffectType::Groove: - stepBeginBasedEffsGlobal_[eff.type] = eff.value; + playbackSpeedEffMem_.enqueue(eff); return false; case EffectType::NoteDelay: if (eff.value < tickCounter_.lock()->getSpeed()) { - stepBeginBasedEffs_[SoundSource::FM].at(static_cast(ch))[EffectType::NoteDelay] = eff.value; + effOnStepBeginMem_[SoundSource::FM].at(static_cast(ch)).enqueue(eff); return true; } return false; case EffectType::PositionJump: case EffectType::SongEnd: case EffectType::PatternBreak: - stepEndBasedEffsGlobal_[eff.type] = eff.value; + posChangeEffMem_.enqueue(eff); return false; default: storeDirectRegisterSetEffectToQueue(SoundSource::FM, ch, eff); @@ -738,11 +735,11 @@ bool isNoteDelay = false; // Read step beginning based effects - auto& stepBeginBasedEffs = stepBeginBasedEffs_[SoundSource::FM].at(uch); + auto& stepBeginBasedEffs = effOnStepBeginMem_[SoundSource::FM].at(uch); for (const auto& eff : stepBeginBasedEffs) { - switch (eff.first) { + switch (eff.type) { case EffectType::NoteDelay: - ntDlyCntFM_.at(uch) = eff.second; + ntDlyCntFM_.at(uch) = eff.value; isNoteDelay = true; break; default: @@ -753,107 +750,107 @@ // Read note on and step beginning based effects if (!isNoteDelay) { - auto& keyOnBasedEffs = keyOnBasedEffs_[SoundSource::FM].at(uch); + auto& keyOnBasedEffs = effOnKeyOnMem_[SoundSource::FM].at(uch); for (auto& eff : keyOnBasedEffs) { - switch (eff.first) { + switch (eff.type) { case EffectType::Arpeggio: - opnaCtrl_->setArpeggioEffectFM(ch, eff.second >> 4, eff.second & 0x0f); + opnaCtrl_->setArpeggioEffectFM(ch, eff.value >> 4, eff.value & 0x0f); break; case EffectType::PortamentoUp: - opnaCtrl_->setPortamentoEffectFM(ch, eff.second); + opnaCtrl_->setPortamentoEffectFM(ch, eff.value); break; case EffectType::PortamentoDown: - opnaCtrl_->setPortamentoEffectFM(ch, -eff.second); + opnaCtrl_->setPortamentoEffectFM(ch, -eff.value); break; case EffectType::TonePortamento: - opnaCtrl_->setPortamentoEffectFM(ch, eff.second, true); + opnaCtrl_->setPortamentoEffectFM(ch, eff.value, true); break; case EffectType::Vibrato: - opnaCtrl_->setVibratoEffectFM(ch, eff.second >> 4, eff.second & 0x0f); + opnaCtrl_->setVibratoEffectFM(ch, eff.value >> 4, eff.value & 0x0f); break; case EffectType::Tremolo: - opnaCtrl_->setTremoloEffectFM(ch, eff.second >> 4, eff.second & 0x0f); + opnaCtrl_->setTremoloEffectFM(ch, eff.value >> 4, eff.value & 0x0f); break; case EffectType::Pan: - if (-1 < eff.second && eff.second < 4) opnaCtrl_->setPanFM(ch, eff.second); + if (-1 < eff.value && eff.value < 4) opnaCtrl_->setPanFM(ch, eff.value); break; case EffectType::VolumeSlide: { - int hi = eff.second >> 4; - int low = eff.second & 0x0f; + int hi = eff.value >> 4; + int low = eff.value & 0x0f; if (hi && !low) opnaCtrl_->setVolumeSlideFM(ch, hi, true); // Slide up else if (!hi) opnaCtrl_->setVolumeSlideFM(ch, low, false); // Slide down break; } case EffectType::Detune: - opnaCtrl_->setDetuneFM(ch, eff.second - 0x80); + opnaCtrl_->setDetuneFM(ch, eff.value - 0x80); break; case EffectType::FineDetune: - opnaCtrl_->setFineDetuneFM(ch, eff.second - 0x80); + opnaCtrl_->setFineDetuneFM(ch, eff.value - 0x80); break; case EffectType::NoteSlideUp: - opnaCtrl_->setNoteSlideFM(ch, eff.second >> 4, eff.second & 0x0f); + opnaCtrl_->setNoteSlideFM(ch, eff.value >> 4, eff.value & 0x0f); break; case EffectType::NoteSlideDown: - opnaCtrl_->setNoteSlideFM(ch, eff.second >> 4, -(eff.second & 0x0f)); + opnaCtrl_->setNoteSlideFM(ch, eff.value >> 4, -(eff.value & 0x0f)); break; case EffectType::NoteCut: - ntCutDlyCntFM_.at(uch) = eff.second; + ntCutDlyCntFM_.at(uch) = eff.value; break; case EffectType::TransposeDelay: - tposeDlyCntFM_.at(uch) = (eff.second & 0x70) >> 4; - tposeDlyValueFM_.at(uch) = ((eff.second & 0x80) ? -1 : 1) * (eff.second & 0x0f); + tposeDlyCntFM_.at(uch) = (eff.value & 0x70) >> 4; + tposeDlyValueFM_.at(uch) = ((eff.value & 0x80) ? -1 : 1) * (eff.value & 0x0f); break; case EffectType::VolumeDelay: { - int count = eff.second >> 8; + int count = eff.value >> 8; if (count > 0) { volDlyCntFM_.at(uch) = count; - volDlyValueFM_.at(uch) = eff.second & 0x00ff; + volDlyValueFM_.at(uch) = eff.value & 0x00ff; } break; } case EffectType::FBControl: - if (-1 < eff.second && eff.second < 8) opnaCtrl_->setFBControlFM(ch, eff.second); + if (-1 < eff.value && eff.value < 8) opnaCtrl_->setFBControlFM(ch, eff.value); break; case EffectType::TLControl: { - int op = eff.second >> 8; - int val = eff.second & 0x00ff; + int op = eff.value >> 8; + int val = eff.value & 0x00ff; if (0 < op && op < 5 && -1 < val && val < 128) opnaCtrl_->setTLControlFM(ch, op - 1, val); break; } case EffectType::MLControl: { - int op = eff.second >> 4; - int val = eff.second & 0x0f; + int op = eff.value >> 4; + int val = eff.value & 0x0f; if (0 < op && op < 5 && -1 < val && val < 16) opnaCtrl_->setMLControlFM(ch, op - 1, val); break; } case EffectType::ARControl: { - int op = eff.second >> 8; - int val = eff.second & 0x00ff; + int op = eff.value >> 8; + int val = eff.value & 0x00ff; if (0 < op && op < 5 && -1 < val && val < 32) opnaCtrl_->setARControlFM(ch, op - 1, val); break; } case EffectType::DRControl: { - int op = eff.second >> 8; - int val = eff.second & 0x00ff; + int op = eff.value >> 8; + int val = eff.value & 0x00ff; if (0 < op && op < 5 && -1 < val && val < 32) opnaCtrl_->setDRControlFM(ch, op - 1, val); break; } case EffectType::RRControl: { - int op = eff.second >> 4; - int val = eff.second & 0x0f; + int op = eff.value >> 4; + int val = eff.value & 0x0f; if (0 < op && op < 5 && -1 < val && val < 16) opnaCtrl_->setRRControlFM(ch, op - 1, val); break; } case EffectType::Brightness: { - if (0 < eff.second) opnaCtrl_->setBrightnessFM(ch, eff.second - 0x80); + if (0 < eff.value) opnaCtrl_->setBrightnessFM(ch, eff.value - 0x80); break; } default: @@ -888,22 +885,22 @@ case EffectType::HardEnvHighPeriod: case EffectType::HardEnvLowPeriod: case EffectType::AutoEnvelope: - keyOnBasedEffs_[SoundSource::SSG].at(static_cast(ch))[eff.type] = eff.value; + effOnKeyOnMem_[SoundSource::SSG].at(static_cast(ch)).enqueue(eff); return false; case EffectType::SpeedTempoChange: case EffectType::Groove: - stepBeginBasedEffsGlobal_[eff.type] = eff.value; + playbackSpeedEffMem_.enqueue(eff); return false; case EffectType::NoteDelay: if (eff.value < tickCounter_.lock()->getSpeed()) { - stepBeginBasedEffs_[SoundSource::SSG].at(static_cast(ch))[EffectType::NoteDelay] = eff.value; + effOnStepBeginMem_[SoundSource::SSG].at(static_cast(ch)).enqueue(eff); return true; } return false; case EffectType::PositionJump: case EffectType::SongEnd: case EffectType::PatternBreak: - stepEndBasedEffsGlobal_[eff.type] = eff.value; + posChangeEffMem_.enqueue(eff); return false; default: storeDirectRegisterSetEffectToQueue(SoundSource::SSG, ch, eff); @@ -917,11 +914,11 @@ bool isNoteDelay = false; // Read step beginning based effects - auto& stepBeginBasedEffs = stepBeginBasedEffs_[SoundSource::SSG].at(uch); + auto& stepBeginBasedEffs = effOnStepBeginMem_[SoundSource::SSG].at(uch); for (const auto& eff : stepBeginBasedEffs) { - switch (eff.first) { + switch (eff.type) { case EffectType::NoteDelay: - ntDlyCntSSG_.at(uch) = eff.second; + ntDlyCntSSG_.at(uch) = eff.value; isNoteDelay = true; break; default: @@ -932,77 +929,77 @@ // Read note on and step beginning based effects if (!isNoteDelay) { - auto& keyOnBasedEffs = keyOnBasedEffs_[SoundSource::SSG].at(uch); + auto& keyOnBasedEffs = effOnKeyOnMem_[SoundSource::SSG].at(uch); for (const auto& eff : keyOnBasedEffs) { - switch (eff.first) { + switch (eff.type) { case EffectType::Arpeggio: - opnaCtrl_->setArpeggioEffectSSG(ch, eff.second >> 4, eff.second & 0x0f); + opnaCtrl_->setArpeggioEffectSSG(ch, eff.value >> 4, eff.value & 0x0f); break; case EffectType::PortamentoUp: - opnaCtrl_->setPortamentoEffectSSG(ch, eff.second); + opnaCtrl_->setPortamentoEffectSSG(ch, eff.value); break; case EffectType::PortamentoDown: - opnaCtrl_->setPortamentoEffectSSG(ch, -eff.second); + opnaCtrl_->setPortamentoEffectSSG(ch, -eff.value); break; case EffectType::TonePortamento: - opnaCtrl_->setPortamentoEffectSSG(ch, eff.second, true); + opnaCtrl_->setPortamentoEffectSSG(ch, eff.value, true); break; case EffectType::Vibrato: - opnaCtrl_->setVibratoEffectSSG(ch, eff.second >> 4, eff.second & 0x0f); + opnaCtrl_->setVibratoEffectSSG(ch, eff.value >> 4, eff.value & 0x0f); break; case EffectType::Tremolo: - opnaCtrl_->setTremoloEffectSSG(ch, eff.second >> 4, eff.second & 0x0f); + opnaCtrl_->setTremoloEffectSSG(ch, eff.value >> 4, eff.value & 0x0f); break; case EffectType::VolumeSlide: { - int hi = eff.second >> 4; - int low = eff.second & 0x0f; + int hi = eff.value >> 4; + int low = eff.value & 0x0f; if (hi && !low) opnaCtrl_->setVolumeSlideSSG(ch, hi, true); // Slide up else if (!hi) opnaCtrl_->setVolumeSlideSSG(ch, low, false); // Slide down break; } case EffectType::Detune: - opnaCtrl_->setDetuneSSG(ch, eff.second - 0x80); + opnaCtrl_->setDetuneSSG(ch, eff.value - 0x80); break; case EffectType::FineDetune: - opnaCtrl_->setFineDetuneSSG(ch, eff.second - 0x80); + opnaCtrl_->setFineDetuneSSG(ch, eff.value - 0x80); break; case EffectType::NoteSlideUp: - opnaCtrl_->setNoteSlideSSG(ch, eff.second >> 4, eff.second & 0x0f); + opnaCtrl_->setNoteSlideSSG(ch, eff.value >> 4, eff.value & 0x0f); break; case EffectType::NoteSlideDown: - opnaCtrl_->setNoteSlideSSG(ch, eff.second >> 4, -(eff.second & 0x0f)); + opnaCtrl_->setNoteSlideSSG(ch, eff.value >> 4, -(eff.value & 0x0f)); break; case EffectType::NoteCut: - ntCutDlyCntSSG_.at(uch) = eff.second; + ntCutDlyCntSSG_.at(uch) = eff.value; break; case EffectType::TransposeDelay: - tposeDlyCntSSG_.at(uch) = (eff.second & 0x70) >> 4; - tposeDlyValueSSG_.at(uch) = ((eff.second & 0x80) ? -1 : 1) * (eff.second & 0x0f); + tposeDlyCntSSG_.at(uch) = (eff.value & 0x70) >> 4; + tposeDlyValueSSG_.at(uch) = ((eff.value & 0x80) ? -1 : 1) * (eff.value & 0x0f); break; case EffectType::ToneNoiseMix: - if (-1 < eff.second && eff.second < 4) opnaCtrl_->setToneNoiseMixSSG(ch, eff.second); + if (-1 < eff.value && eff.value < 4) opnaCtrl_->setToneNoiseMixSSG(ch, eff.value); break; case EffectType::NoisePitch: - if (-1 < eff.second && eff.second < 32) opnaCtrl_->setNoisePitchSSG(ch, eff.second); + if (-1 < eff.value && eff.value < 32) opnaCtrl_->setNoisePitchSSG(ch, eff.value); break; case EffectType::HardEnvHighPeriod: - opnaCtrl_->setHardEnvelopePeriod(ch, true, eff.second); + opnaCtrl_->setHardEnvelopePeriod(ch, true, eff.value); break; case EffectType::HardEnvLowPeriod: - opnaCtrl_->setHardEnvelopePeriod(ch, false, eff.second); + opnaCtrl_->setHardEnvelopePeriod(ch, false, eff.value); break; case EffectType::VolumeDelay: { - int count = eff.second >> 8; + int count = eff.value >> 8; if (count > 0) { volDlyCntSSG_.at(uch) = count; - volDlyValueSSG_.at(uch) = eff.second & 0x00ff; + volDlyValueSSG_.at(uch) = eff.value & 0x00ff; } break; } case EffectType::AutoEnvelope: - opnaCtrl_->setAutoEnvelopeSSG(ch, (eff.second >> 4) - 8, eff.second & 0x0f); + opnaCtrl_->setAutoEnvelopeSSG(ch, (eff.value >> 4) - 8, eff.value & 0x0f); break; default: break; @@ -1021,22 +1018,22 @@ case EffectType::NoteCut: case EffectType::MasterVolume: case EffectType::VolumeDelay: - keyOnBasedEffs_[SoundSource::RHYTHM].at(static_cast(ch))[eff.type] = eff.value; + effOnKeyOnMem_[SoundSource::RHYTHM].at(static_cast(ch)).enqueue(eff); return false; case EffectType::SpeedTempoChange: case EffectType::Groove: - stepBeginBasedEffsGlobal_[eff.type] = eff.value; + playbackSpeedEffMem_.enqueue(eff); return false; case EffectType::NoteDelay: if (eff.value < tickCounter_.lock()->getSpeed()) { - stepBeginBasedEffs_[SoundSource::RHYTHM].at(static_cast(ch))[EffectType::NoteDelay] = eff.value; + effOnStepBeginMem_[SoundSource::RHYTHM].at(static_cast(ch)).enqueue(eff); return true; } return false; case EffectType::PositionJump: case EffectType::SongEnd: case EffectType::PatternBreak: - stepEndBasedEffsGlobal_[eff.type] = eff.value; + posChangeEffMem_.enqueue(eff); return false; default: storeDirectRegisterSetEffectToQueue(SoundSource::RHYTHM, ch, eff); @@ -1050,11 +1047,11 @@ bool isNoteDelay = false; // Read step beginning based effects - auto& stepBeginBasedEffs = stepBeginBasedEffs_[SoundSource::RHYTHM].at(uch); + auto& stepBeginBasedEffs = effOnStepBeginMem_[SoundSource::RHYTHM].at(uch); for (const auto& eff : stepBeginBasedEffs) { - switch (eff.first) { + switch (eff.type) { case EffectType::NoteDelay: - ntDlyCntRhythm_.at(uch) = eff.second; + ntDlyCntRhythm_.at(uch) = eff.value; isNoteDelay = true; break; default: @@ -1065,24 +1062,24 @@ // Read key on and step beginning based effects if (!isNoteDelay) { - auto& keyOnBasedEffs = keyOnBasedEffs_[SoundSource::RHYTHM].at(uch); + auto& keyOnBasedEffs = effOnKeyOnMem_[SoundSource::RHYTHM].at(uch); for (const auto& eff : keyOnBasedEffs) { - switch (eff.first) { + switch (eff.type) { case EffectType::Pan: - if (-1 < eff.second && eff.second < 4) opnaCtrl_->setPanRhythm(ch, eff.second); + if (-1 < eff.value && eff.value < 4) opnaCtrl_->setPanRhythm(ch, eff.value); break; case EffectType::NoteCut: - ntCutDlyCntRhythm_.at(uch) = eff.second; + ntCutDlyCntRhythm_.at(uch) = eff.value; break; case EffectType::MasterVolume: - if (-1 < eff.second && eff.second < 64) opnaCtrl_->setMasterVolumeRhythm(eff.second); + if (-1 < eff.value && eff.value < 64) opnaCtrl_->setMasterVolumeRhythm(eff.value); break; case EffectType::VolumeDelay: { - int count = eff.second >> 8; + int count = eff.value >> 8; if (count > 0) { volDlyCntRhythm_.at(uch) = count; - volDlyValueRhythm_.at(uch) = eff.second & 0x00ff; + volDlyValueRhythm_.at(uch) = eff.value & 0x00ff; } break; } @@ -1114,22 +1111,22 @@ case EffectType::NoteCut: case EffectType::TransposeDelay: case EffectType::VolumeDelay: - keyOnBasedEffs_[SoundSource::ADPCM].front()[eff.type] = eff.value; + effOnKeyOnMem_[SoundSource::ADPCM].front().enqueue(eff); return false; case EffectType::SpeedTempoChange: case EffectType::Groove: - stepBeginBasedEffsGlobal_[eff.type] = eff.value; + playbackSpeedEffMem_.enqueue(eff); return false; case EffectType::NoteDelay: if (eff.value < tickCounter_.lock()->getSpeed()) { - stepBeginBasedEffs_[SoundSource::ADPCM].front()[EffectType::NoteDelay] = eff.value; + effOnStepBeginMem_[SoundSource::ADPCM].front().enqueue(eff); return true; } return false; case EffectType::PositionJump: case EffectType::SongEnd: case EffectType::PatternBreak: - stepEndBasedEffsGlobal_[eff.type] = eff.value; + posChangeEffMem_.enqueue(eff); return false; default: storeDirectRegisterSetEffectToQueue(SoundSource::ADPCM, ch, eff); @@ -1142,11 +1139,11 @@ bool isNoteDelay = false; // Read step beginning based effects - auto& stepBeginBasedEffs = stepBeginBasedEffs_[SoundSource::ADPCM].front(); + auto& stepBeginBasedEffs = effOnStepBeginMem_[SoundSource::ADPCM].front(); for (const auto& eff : stepBeginBasedEffs) { - switch (eff.first) { + switch (eff.type) { case EffectType::NoteDelay: - ntDlyCntADPCM_ = eff.second; + ntDlyCntADPCM_ = eff.value; isNoteDelay = true; break; default: @@ -1157,63 +1154,63 @@ // Read note on and step beginning based effects if (!isNoteDelay) { - auto& keyOnBasedEffs = keyOnBasedEffs_[SoundSource::ADPCM].front(); + auto& keyOnBasedEffs = effOnKeyOnMem_[SoundSource::ADPCM].front(); for (const auto& eff : keyOnBasedEffs) { - switch (eff.first) { + switch (eff.type) { case EffectType::Arpeggio: - opnaCtrl_->setArpeggioEffectADPCM(eff.second >> 4, eff.second & 0x0f); + opnaCtrl_->setArpeggioEffectADPCM(eff.value >> 4, eff.value & 0x0f); break; case EffectType::PortamentoUp: - opnaCtrl_->setPortamentoEffectADPCM(eff.second); + opnaCtrl_->setPortamentoEffectADPCM(eff.value); break; case EffectType::PortamentoDown: - opnaCtrl_->setPortamentoEffectADPCM(-eff.second); + opnaCtrl_->setPortamentoEffectADPCM(-eff.value); break; case EffectType::TonePortamento: - opnaCtrl_->setPortamentoEffectADPCM(eff.second, true); + opnaCtrl_->setPortamentoEffectADPCM(eff.value, true); break; case EffectType::Vibrato: - opnaCtrl_->setVibratoEffectADPCM(eff.second >> 4, eff.second & 0x0f); + opnaCtrl_->setVibratoEffectADPCM(eff.value >> 4, eff.value & 0x0f); break; case EffectType::Tremolo: - opnaCtrl_->setTremoloEffectADPCM(eff.second >> 4, eff.second & 0x0f); + opnaCtrl_->setTremoloEffectADPCM(eff.value >> 4, eff.value & 0x0f); break; case EffectType::Pan: - if (-1 < eff.second && eff.second < 4) opnaCtrl_->setPanADPCM(eff.second); + if (-1 < eff.value && eff.value < 4) opnaCtrl_->setPanADPCM(eff.value); break; case EffectType::VolumeSlide: { - int hi = eff.second >> 4; - int low = eff.second & 0x0f; + int hi = eff.value >> 4; + int low = eff.value & 0x0f; if (hi && !low) opnaCtrl_->setVolumeSlideADPCM(hi, true); // Slide up else if (!hi) opnaCtrl_->setVolumeSlideADPCM(low, false); // Slide down break; } case EffectType::Detune: - opnaCtrl_->setDetuneADPCM(eff.second - 0x80); + opnaCtrl_->setDetuneADPCM(eff.value - 0x80); break; case EffectType::FineDetune: - opnaCtrl_->setFineDetuneADPCM(eff.second - 0x80); + opnaCtrl_->setFineDetuneADPCM(eff.value - 0x80); break; case EffectType::NoteSlideUp: - opnaCtrl_->setNoteSlideADPCM(eff.second >> 4, eff.second & 0x0f); + opnaCtrl_->setNoteSlideADPCM(eff.value >> 4, eff.value & 0x0f); break; case EffectType::NoteSlideDown: - opnaCtrl_->setNoteSlideADPCM(eff.second >> 4, -(eff.second & 0x0f)); + opnaCtrl_->setNoteSlideADPCM(eff.value >> 4, -(eff.value & 0x0f)); break; case EffectType::NoteCut: - ntCutDlyCntADPCM_ = eff.second; + ntCutDlyCntADPCM_ = eff.value; break; case EffectType::TransposeDelay: - tposeDlyCntADPCM_ = (eff.second & 0x70) >> 4; - tposeDlyValueADPCM_ = ((eff.second & 0x80) ? -1 : 1) * (eff.second & 0x0f); + tposeDlyCntADPCM_ = (eff.value & 0x70) >> 4; + tposeDlyValueADPCM_ = ((eff.value & 0x80) ? -1 : 1) * (eff.value & 0x0f); break; case EffectType::VolumeDelay: { - int count = eff.second >> 8; + int count = eff.value >> 8; if (count > 0) { volDlyCntADPCM_ = count; - volDlyValueADPCM_ = eff.second & 0x00ff; + volDlyValueADPCM_ = eff.value & 0x00ff; } break; } @@ -1267,8 +1264,7 @@ bool PlaybackManager::effPositionJump(int nextOrder) { if (nextOrder < static_cast(getOrderSize(curSongNum_))) { - nextReadOrder_ = nextOrder; - nextReadStep_ = 0; + nextReadPos_.set(nextOrder, 0); return true; } return false; @@ -1276,21 +1272,18 @@ void PlaybackManager::effSongEnd() { - nextReadOrder_ = -1; - nextReadStep_ = -1; + nextReadPos_.invalidate(); } bool PlaybackManager::effPatternBreak(int nextStep) { - if (playOrderNum_ == static_cast(getOrderSize(curSongNum_)) - 1 + if (playingPos_.order == static_cast(getOrderSize(curSongNum_)) - 1 && nextStep < static_cast(getPatternSizeFromOrderNumber(curSongNum_, 0))) { - nextReadOrder_ = 0; - nextReadStep_ = nextStep; + nextReadPos_.set(0, nextStep); return true; } - else if (nextStep < static_cast(getPatternSizeFromOrderNumber(curSongNum_, playOrderNum_ + 1))) { - nextReadOrder_ = playOrderNum_ + 1; - nextReadStep_ = nextStep; + else if (nextStep < static_cast(getPatternSizeFromOrderNumber(curSongNum_, playingPos_.order + 1))) { + nextReadPos_.set(playingPos_.order + 1, nextStep); return true; } return false; @@ -1310,7 +1303,7 @@ void PlaybackManager::effGrooveChange(int num) { - tickCounter_.lock()->setGroove(mod_.lock()->getGroove(num).getSequence()); + tickCounter_.lock()->setGroove(mod_.lock()->getGroove(num)); tickCounter_.lock()->setGrooveState(GrooveState::ValidByLocal); } @@ -1323,7 +1316,7 @@ auto& song = mod_.lock()->getSong(curSongNum_); for (auto& attrib : songStyle_.trackAttribs) { auto& curStep = song.getTrack(attrib.number) - .getPatternFromOrderNumber(playOrderNum_).getStep(playStepNum_); + .getPatternFromOrderNumber(playingPos_.order).getStep(playingPos_.step); int ch = attrib.channelInSource; switch (attrib.source) { case SoundSource::FM: checkFMDelayEventsInTick(curStep, ch); break; @@ -1332,12 +1325,12 @@ case SoundSource::ADPCM: checkADPCMDelayEventsInTick(curStep); break; } - if (rest == 1 && nextReadOrder_ != -1 && attrib.source == SoundSource::FM && !isPlayingStep()) { + if (rest == 1 && nextReadPos_.isValid() && attrib.source == SoundSource::FM && !isPlayingStep()) { // Channel envelope reset before next key on auto& step = song.getTrack(attrib.number) - .getPatternFromOrderNumber(nextReadOrder_).getStep(nextReadStep_); - for (int i = 0; i < 4; ++i) { - auto&& eff = Effect::makeEffectData(attrib.source, step.getEffectID(i), step.getEffectValue(i)); + .getPatternFromOrderNumber(nextReadPos_.order).getStep(nextReadPos_.step); + for (int i = 0; i < Step::N_EFFECT; ++i) { + auto&& eff = effect_utils::validateEffect(attrib.source, step.getEffect(i)); if (eff.type == EffectType::NoteDelay && eff.value > 0) { // Note delay check opnaCtrl_->tickEvent(attrib.source, ch); return; @@ -1352,12 +1345,12 @@ opnaCtrl_->updateRegisterStates(); } -void PlaybackManager::checkFMDelayEventsInTick(Step& step, int ch) +void PlaybackManager::checkFMDelayEventsInTick(const Step& step, int ch) { size_t uch = static_cast(ch); // Check volume delay if (!volDlyCntFM_.at(uch)) - opnaCtrl_->setTemporaryVolumeFM(ch, volDlyValueFM_.at(uch)); + opnaCtrl_->setOneshotVolumeFM(ch, volDlyValueFM_.at(uch)); // Check note cut if (!ntCutDlyCntFM_.at(uch)) opnaCtrl_->keyOffFM(ch); @@ -1368,7 +1361,7 @@ checkFMNoteDelayAndEnvelopeReset(step, ch); } -void PlaybackManager::checkFMNoteDelayAndEnvelopeReset(Step& step, int ch) +void PlaybackManager::checkFMNoteDelayAndEnvelopeReset(const Step& step, int ch) { int cnt = ntDlyCntFM_.at(static_cast(ch)); if (!cnt) { @@ -1380,14 +1373,12 @@ } } -void PlaybackManager::envelopeResetEffectFM(Step& step, int ch) +void PlaybackManager::envelopeResetEffectFM(const Step& step, int ch) { - int n = step.getNoteNumber(); - if ((n >= 0 || n < -2) + if (!(step.isEmptyNote() || step.hasKeyOff()) && opnaCtrl_->enableFMEnvelopeReset(ch)) { // Key on or echo buffer access - for (int i = 0; i < 4; ++i) { - auto&& eff = Effect::makeEffectData( // "SoundSource::FM" is dummy - SoundSource::FM, step.getEffectID(i), step.getEffectValue(i)); + for (int i = 0; i < Step::N_EFFECT; ++i) { + auto&& eff = effect_utils::validateEffect(SoundSource::FM, step.getEffect(i)); // "SoundSource::FM" is dummy if (eff.type == EffectType::TonePortamento) { if (eff.value) opnaCtrl_->tickEvent(SoundSource::FM, ch); else opnaCtrl_->resetFMChannelEnvelope(ch); @@ -1402,12 +1393,12 @@ } } -void PlaybackManager::checkSSGDelayEventsInTick(Step& step, int ch) +void PlaybackManager::checkSSGDelayEventsInTick(const Step& step, int ch) { size_t uch = static_cast(ch); // Check volume delay if (!volDlyCntSSG_.at(uch)) - opnaCtrl_->setTemporaryVolumeSSG(ch, volDlyValueSSG_.at(uch)); + opnaCtrl_->setOneshotVolumeSSG(ch, volDlyValueSSG_.at(uch)); // Check note cut if (!ntCutDlyCntSSG_.at(uch)) opnaCtrl_->keyOffSSG(ch); @@ -1419,12 +1410,12 @@ executeSSGStepEvents(step, ch, true); } -void PlaybackManager::checkRhythmDelayEventsInTick(Step& step, int ch) +void PlaybackManager::checkRhythmDelayEventsInTick(const Step& step, int ch) { size_t uch = static_cast(ch); // Check volume delay if (!volDlyCntRhythm_.at(uch)) - opnaCtrl_->setTemporaryVolumeRhythm(ch, volDlyValueRhythm_.at(uch)); + opnaCtrl_->setOneshotVolumeRhythm(ch, volDlyValueRhythm_.at(uch)); // Check note cut if (!ntCutDlyCntRhythm_.at(uch)) opnaCtrl_->setKeyOnFlagRhythm(ch); @@ -1433,11 +1424,11 @@ executeRhythmStepEvents(step, ch, true); } -void PlaybackManager::checkADPCMDelayEventsInTick(Step& step) +void PlaybackManager::checkADPCMDelayEventsInTick(const Step& step) { // Check volume delay if (!volDlyCntADPCM_) - opnaCtrl_->setTemporaryVolumeADPCM(volDlyValueADPCM_); + opnaCtrl_->setOneshotVolumeADPCM(volDlyValueADPCM_); // Check note cut if (!ntCutDlyCntADPCM_) opnaCtrl_->keyOffADPCM(); @@ -1451,14 +1442,14 @@ void PlaybackManager::clearEffectMaps() { - stepBeginBasedEffsGlobal_.clear(); - stepEndBasedEffsGlobal_.clear(); + playbackSpeedEffMem_.clear(); + posChangeEffMem_.clear(); - for (auto& maps: keyOnBasedEffs_) { - for (EffectMemory& map : maps.second) map.clear(); + for (auto& maps: effOnKeyOnMem_) { + for (EffectMemory& mem : maps.second) mem.clear(); } - for (auto& maps: stepBeginBasedEffs_) { - for (EffectMemory& map : maps.second) map.clear(); + for (auto& maps: effOnStepBeginMem_) { + for (EffectMemory& mem : maps.second) mem.clear(); } for (auto& maps: directRegisterSets_) { for (DirectRegisterSetQueue& queue : maps.second) queue.clear(); @@ -1557,8 +1548,8 @@ void PlaybackManager::checkPlayPosition(int maxStepSize) { - if (isPlaySong() && playStepNum_ >= maxStepSize) { - playStepNum_ = maxStepSize - 1; + if (isPlaySong() && playingPos_.step >= maxStepSize) { + playingPos_.step = maxStepSize - 1; findNextStep(); } } @@ -1570,18 +1561,9 @@ void PlaybackManager::retrieveChannelStates() { - size_t fmch = getFMChannelCount(songStyle_.type); + size_t fmch = Song::getFMChannelCount(songStyle_.type); - std::vector tonesCntFM(fmch), tonesCntSSG(3); - int tonesCntADPCM = 0; - std::vector> toneFM(fmch), toneSSG(3); - std::vector toneADPCM = std::vector(3, -1); - for (size_t i = 0; i < fmch; ++i) { - toneFM.at(i) = std::vector(3, -1); - } - for (size_t i = 0; i < 3; ++i) { - toneSSG.at(i) = std::vector(3, -1); - } + // Skip echo buffer due to takes time to search std::vector isSetInstFM(fmch, false), isSetVolFM(fmch, false), isSetArpFM(fmch, false); std::vector isSetPrtFM(fmch, false), isSetVibFM(fmch, false), isSetTreFM(fmch, false); std::vector isSetPanFM(fmch, false), isSetVolSldFM(fmch, false), isSetDtnFM(fmch, false); @@ -1605,14 +1587,13 @@ /// bit2: groove uint8_t speedStates = 0; - int o = playOrderNum_; - int s = playStepNum_; + Position pos = playingPos_; bool isPrevPos = false; Song& song = mod_.lock()->getSong(curSongNum_); while (true) { for (auto it = songStyle_.trackAttribs.rbegin(), e = songStyle_.trackAttribs.rend(); it != e; ++it) { - Step& step = song.getTrack(it->number).getPatternFromOrderNumber(o).getStep(s); + Step& step = song.getTrack(it->number).getPatternFromOrderNumber(pos.order).getStep(pos.step); int ch = it->channelInSource; size_t uch = static_cast(ch); @@ -1621,13 +1602,13 @@ { // Volume int vol = step.getVolume(); - if (!isSetVolFM[uch] && 0 <= vol && vol < 0x80) { + if (!isSetVolFM[uch] && step.hasVolume() && vol < bt_defs::NSTEP_FM_VOLUME) { isSetVolFM[uch] = true; if (isPrevPos) - opnaCtrl_->setVolumeFM(ch, step.getVolume()); + opnaCtrl_->setVolumeFM(ch, vol); } // Instrument - if (!isSetInstFM[uch] && step.getInstrumentNumber() != -1) { + if (!isSetInstFM[uch] && step.hasInstrument()) { if (auto inst = std::dynamic_pointer_cast( instMan_.lock()->getInstrumentSharedPtr(step.getInstrumentNumber()))) { isSetInstFM[uch] = true; @@ -1636,8 +1617,8 @@ } } // Effects - for (int i = 3; i > -1; --i) { - Effect eff = Effect::makeEffectData(SoundSource::FM, step.getEffectID(i), step.getEffectValue(i)); + for (int i = Step::N_EFFECT - 1; i > -1; --i) { + Effect eff = effect_utils::validateEffect(SoundSource::FM, step.getEffect(i)); switch (eff.type) { case EffectType::Arpeggio: if (!isSetArpFM[uch]) { @@ -1799,36 +1780,19 @@ break; } } - // Tone - int t = step.getNoteNumber(); - if (isPrevPos && t != -1 && t != -2) { - --tonesCntFM[uch]; - for (auto it2 = toneFM[uch].rbegin(); - it2 != toneFM[uch].rend(); ++it2) { - if (*it2 == -1 || *it2 == tonesCntFM[uch]) { - if (t >= 0) { - *it2 = t; - } - else if (t < -2) { - *it2 = tonesCntFM[uch] - t + 2; - } - break; - } - } - } break; } case SoundSource::SSG: { // Volume int vol = step.getVolume(); - if (!isSetVolSSG[uch] && 0 <= vol && vol < 0x10) { + if (!isSetVolSSG[uch] && step.hasVolume() && vol < bt_defs::NSTEP_SSG_VOLUME) { isSetVolSSG[uch] = true; if (isPrevPos) opnaCtrl_->setVolumeSSG(ch, vol); } // Instrument - if (!isSetInstSSG[uch] && step.getInstrumentNumber() != -1) { + if (!isSetInstSSG[uch] && step.hasInstrument()) { if (auto inst = std::dynamic_pointer_cast( instMan_.lock()->getInstrumentSharedPtr(step.getInstrumentNumber()))) { isSetInstSSG[uch] = true; @@ -1837,8 +1801,8 @@ } } // Effects - for (int i = 3; i > -1; --i) { - Effect eff = Effect::makeEffectData(SoundSource::SSG, step.getEffectID(i), step.getEffectValue(i)); + for (int i = Step::N_EFFECT - 1; i > -1; --i) { + Effect eff = effect_utils::validateEffect(SoundSource::SSG, step.getEffect(i)); switch (eff.type) { case EffectType::Arpeggio: if (!isSetArpSSG[uch]) { @@ -1953,37 +1917,20 @@ break; } } - // Tone - int t = step.getNoteNumber(); - if (isPrevPos && t != -1 && t != -2) { - --tonesCntSSG[uch]; - for (auto it2 = toneSSG[uch].rbegin(); - it2 != toneSSG[uch].rend(); ++it2) { - if (*it2 == -1 || *it2 == tonesCntSSG[uch]) { - if (t >= 0) { - *it2 = t; - } - else if (t < -2) { - *it2 = tonesCntSSG[uch] - t + 2; - } - break; - } - } - } break; } case SoundSource::RHYTHM: { // Volume int vol = step.getVolume(); - if (!isSetVolRhythm[uch] && 0 <= vol && vol < 0x20) { + if (!isSetVolRhythm[uch] && step.hasVolume() && vol < bt_defs::NSTEP_RHYTHM_VOLUME) { isSetVolRhythm[uch] = true; if (isPrevPos) opnaCtrl_->setVolumeRhythm(ch, vol); } // Effects - for (int i = 3; i > -1; --i) { - Effect eff = Effect::makeEffectData(SoundSource::RHYTHM, step.getEffectID(i), step.getEffectValue(i)); + for (int i = Step::N_EFFECT - 1; i > -1; --i) { + Effect eff = effect_utils::validateEffect(SoundSource::RHYTHM, step.getEffect(i)); switch (eff.type) { case EffectType::Pan: if (-1 < eff.value && eff.value < 4 && !isSetPanRhythm[uch]) { @@ -2027,13 +1974,13 @@ { // Volume int vol = step.getVolume(); - if (!isSetVolADPCM && 0 <= vol && vol < 0x100) { + if (!isSetVolADPCM && step.hasVolume() && vol < bt_defs::NSTEP_ADPCM_VOLUME) { isSetVolADPCM = true; if (isPrevPos) - opnaCtrl_->setVolumeADPCM(step.getVolume()); + opnaCtrl_->setVolumeADPCM(vol); } // Instrument - if (!isSetInstADPCM && step.getInstrumentNumber() != -1) { + if (!isSetInstADPCM && step.hasInstrument()) { if (auto inst = std::dynamic_pointer_cast( instMan_.lock()->getInstrumentSharedPtr(step.getInstrumentNumber()))) { isSetInstADPCM = true; @@ -2042,8 +1989,8 @@ } } // Effects - for (int i = 3; i > -1; --i) { - Effect eff = Effect::makeEffectData(SoundSource::ADPCM, step.getEffectID(i), step.getEffectValue(i)); + for (int i = Step::N_EFFECT - 1; i > -1; --i) { + Effect eff = effect_utils::validateEffect(SoundSource::ADPCM, step.getEffect(i)); switch (eff.type) { case EffectType::Arpeggio: if (!isSetArpADPCM) { @@ -2132,23 +2079,6 @@ break; } } - // Tone - int t = step.getNoteNumber(); - if (isPrevPos && t != -1 && t != -2) { - --tonesCntADPCM; - for (auto it2 = toneADPCM.rbegin(); - it2 != toneADPCM.rend(); ++it2) { - if (*it2 == -1 || *it2 == tonesCntADPCM) { - if (t >= 0) { - *it2 = t; - } - else if (t < -2) { - *it2 = tonesCntADPCM - t + 2; - } - break; - } - } - } break; } } @@ -2156,37 +2086,19 @@ // Move position isPrevPos = true; - if (--s < 0) { - if (--o < 0) break; - s = static_cast(getPatternSizeFromOrderNumber(curSongNum_, o)) - 1; + if (--pos.step < 0) { + if (--pos.order < 0) break; + pos.step = static_cast(getPatternSizeFromOrderNumber(curSongNum_, pos.order)) - 1; } } - // Echo & sequence reset + // Sequence reset for (size_t ch = 0; ch < fmch; ++ch) { - for (size_t i = 0; i < 3; ++i) { - if (toneFM.at(ch).at(i) >= 0) { - std::pair octNote = noteNumberToOctaveAndNote(toneFM[ch][i]); - opnaCtrl_->updateEchoBufferFM(static_cast(ch), octNote.first, octNote.second, 0); - } - } opnaCtrl_->haltSequencesFM(static_cast(ch)); } for (size_t ch = 0; ch < 3; ++ch) { - for (size_t i = 0; i < 3; ++i) { - if (toneSSG.at(ch).at(i) >= 0) { - std::pair octNote = noteNumberToOctaveAndNote(toneSSG[ch][i]); - opnaCtrl_->updateEchoBufferSSG(static_cast(ch), octNote.first, octNote.second, 0); - } - } opnaCtrl_->haltSequencesSSG(static_cast(ch)); } - for (size_t i = 0; i < 3; ++i) { - if (toneADPCM.at(i) >= 0) { - std::pair octNote = noteNumberToOctaveAndNote(toneADPCM[i]); - opnaCtrl_->updateEchoBufferADPCM(octNote.first, octNote.second, 0); - } - } opnaCtrl_->haltSequencesADPCM(); } diff -Nru bambootracker-0.4.5+git20121209/BambooTracker/playback.hpp bambootracker-0.4.6/BambooTracker/playback.hpp --- bambootracker-0.4.5+git20121209/BambooTracker/playback.hpp 2020-12-08 14:05:28.000000000 +0000 +++ bambootracker-0.4.6/BambooTracker/playback.hpp 2021-02-11 14:37:17.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (C) 2019-2020 Rerrah + * Copyright (C) 2019-2021 Rerrah * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation @@ -32,11 +32,37 @@ #include "module.hpp" #include "effect.hpp" #include "enum_hash.hpp" +#include "bamboo_tracker_defs.hpp" class OPNAController; class InstrumentsManager; class TickCounter; +class EffectMemory +{ +public: + EffectMemory(); + void enqueue(const Effect& eff); + void clear(); + + using container_type = std::vector; + using reference = container_type::reference; + using const_reference = container_type::const_reference; + using iterator = container_type::iterator; + using const_iterator = container_type::const_iterator; + using value_type = container_type::value_type; + + iterator begin() noexcept { return mem_.begin(); } + const_iterator begin() const noexcept { return mem_.begin(); } + const_iterator cbegin() const noexcept { return mem_.cbegin(); } + iterator end() noexcept { return mem_.end(); } + const_iterator end() const noexcept { return mem_.end(); } + const_iterator cend() const noexcept { return mem_.cend(); } + +private: + std::vector mem_; +}; + class PlaybackManager { public: @@ -79,8 +105,20 @@ int curSongNum_; SongStyle songStyle_; - int playOrderNum_, playStepNum_; - int nextReadOrder_, nextReadStep_; + + struct Position + { + int order, step; + static constexpr int INVALID = -1; + Position(int o, int s) : order(o), step(s) {} + void set(int o, int s) // Faster than making and copying a new instance + { + order = o; + step = s; + } + void invalidate() noexcept { set(INVALID, INVALID); } + bool isValid() const noexcept { return (order > INVALID && step > INVALID); } + } playingPos_, nextReadPos_; uint8_t playStateFlags_; @@ -95,22 +133,19 @@ void stepProcess(); std::unordered_map> isNoteDelay_; - void executeFMStepEvents(Step& step, int ch, bool calledByNoteDelay = false); - void executeSSGStepEvents(Step& step, int ch, bool calledByNoteDelay = false); - void executeRhythmStepEvents(Step& step, int ch, bool calledByNoteDelay = false); - void executeADPCMStepEvents(Step& step, bool calledByNoteDelay = false); - - using EffectMemory = std::unordered_map; - EffectMemory stepBeginBasedEffsGlobal_, stepEndBasedEffsGlobal_; - using EffectMemorySource = std::vector>; - std::unordered_map keyOnBasedEffs_, stepBeginBasedEffs_; + void executeFMStepEvents(const Step& step, int ch, bool calledByNoteDelay = false); + void executeSSGStepEvents(const Step& step, int ch, bool calledByNoteDelay = false); + void executeRhythmStepEvents(const Step& step, int ch, bool calledByNoteDelay = false); + void executeADPCMStepEvents(const Step& step, bool calledByNoteDelay = false); + + EffectMemory playbackSpeedEffMem_, posChangeEffMem_; + std::unordered_map> effOnKeyOnMem_, effOnStepBeginMem_; struct RegisterUnit { int address, value; bool hasCompleted; }; - using DirectRegisterSetQueue = std::vector; using DirectRegisterSetSource = std::vector; std::unordered_map directRegisterSets_; @@ -136,13 +171,12 @@ void tickProcess(int rest); - void checkFMDelayEventsInTick(Step& step, int ch); - void checkFMNoteDelayAndEnvelopeReset(Step& step, int ch); - void envelopeResetEffectFM(Step& step, int ch); - void checkSSGDelayEventsInTick(Step& step, int ch); - void checkRhythmDelayEventsInTick(Step& step, int ch); - void checkADPCMDelayEventsInTick(Step& step); - + void checkFMDelayEventsInTick(const Step& step, int ch); + void checkFMNoteDelayAndEnvelopeReset(const Step& step, int ch); + void envelopeResetEffectFM(const Step& step, int ch); + void checkSSGDelayEventsInTick(const Step& step, int ch); + void checkRhythmDelayEventsInTick(const Step& step, int ch); + void checkADPCMDelayEventsInTick(const Step& step); std::vector ntDlyCntFM_, ntCutDlyCntFM_, volDlyCntFM_; std::vector ntDlyCntSSG_, ntCutDlyCntSSG_, volDlyCntSSG_; diff -Nru bambootracker-0.4.5+git20121209/BambooTracker/resources/doc/doc.qrc bambootracker-0.4.6/BambooTracker/resources/doc/doc.qrc --- bambootracker-0.4.5+git20121209/BambooTracker/resources/doc/doc.qrc 1970-01-01 00:00:00.000000000 +0000 +++ bambootracker-0.4.6/BambooTracker/resources/doc/doc.qrc 2021-02-11 14:37:17.000000000 +0000 @@ -0,0 +1,5 @@ + + + keyboard_shortcuts.html + + diff -Nru bambootracker-0.4.5+git20121209/BambooTracker/resources/doc/keyboard_shortcuts.html bambootracker-0.4.6/BambooTracker/resources/doc/keyboard_shortcuts.html --- bambootracker-0.4.5+git20121209/BambooTracker/resources/doc/keyboard_shortcuts.html 1970-01-01 00:00:00.000000000 +0000 +++ bambootracker-0.4.6/BambooTracker/resources/doc/keyboard_shortcuts.html 2021-02-11 14:37:17.000000000 +0000 @@ -0,0 +1,302 @@ + + + +Keyboard shortcuts + + + + +

General

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
KeyCommand
Ctrl+NCreate new module
Ctrl+OOpen module
Ctrl+SSave module
Ctrl+POpen module property dialog
Ctrl+IOpen current instrument editor
ReturnPlay/stop song
F1Show effect list dialog
F5Play from start
F6Play pattern
F7Play from current position
F8Stop song
SpaceToggle jam/edit mode
Alt+LFocus on instrument list
Alt+OFocus on order list
Alt+PFocus on pattern editor
F12Kill sound
+

Instrument list

+ + + + + + + + + + + + + + + + + + + + + +
KeyCommand
InsertAdd instrument
DeleteRemove instrument
Ctrl+IOpen current instrument editor
+

Order list

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
KeyCommand
Ctrl+CCopy
Ctrl+VPaste
Ctrl+ASelect track/all
Ctrl+DDuplicate order
Alt+DClone order
HomeJump to first order
EndJump to last order
PageUpJump to upper oder
PageDownJump to lower oder
InsertInsert order below
DeleteDelete order
EscapeDeselect
+

Pattern editor

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
KeyCommand
Ctrl+CCopy
Ctrl+XCut
Ctrl+VPaste
Ctrl+MPaste and mix
Ctrl+ASelect track/all
Ctrl+GInterpolate
Ctrl+RReverse
Ctrl+F1Decrease note
Ctrl+F2Increase note
Ctrl+F3Decrease octave
Ctrl+F4Increase octave
Alt+F9Toggle track
Alt+F10Solo track
Alt+SReplace instrument
TabJump to right track
BackTabJump to left track
HomeJump to first step
EndJump to last step
PageUpJump to upper step
PageDownJump to lower step
Ctrl+UpJump to upper 1st highlighted step
Ctrl+DownJump to lower 1st highlighted step
InsertInsert step
BackSpaceDelete the step above
DeleteDelete commands
EscapeDeselection
-Key off
* (numpad)Increase octave/echo buffer number
/ (numpad)Decrease octave/echo buffer number
^Echo buffer access
+ + + Binary files /tmp/tmpGWcJfZ/u8imGnHSZm/bambootracker-0.4.5+git20121209/BambooTracker/resources/icon/BambooTracker.icns and /tmp/tmpGWcJfZ/QnT7Jpno1T/bambootracker-0.4.6/BambooTracker/resources/icon/BambooTracker.icns differ Binary files /tmp/tmpGWcJfZ/u8imGnHSZm/bambootracker-0.4.5+git20121209/BambooTracker/resources/icon/BambooTracker.ico and /tmp/tmpGWcJfZ/QnT7Jpno1T/bambootracker-0.4.6/BambooTracker/resources/icon/BambooTracker.ico differ Binary files /tmp/tmpGWcJfZ/u8imGnHSZm/bambootracker-0.4.5+git20121209/BambooTracker/resources/icon/iconfinder_add_4941.png and /tmp/tmpGWcJfZ/QnT7Jpno1T/bambootracker-0.4.6/BambooTracker/resources/icon/iconfinder_add_4941.png differ Binary files /tmp/tmpGWcJfZ/u8imGnHSZm/bambootracker-0.4.5+git20121209/BambooTracker/resources/icon/iconfinder_arrow_down_4982.png and /tmp/tmpGWcJfZ/QnT7Jpno1T/bambootracker-0.4.6/BambooTracker/resources/icon/iconfinder_arrow_down_4982.png differ Binary files /tmp/tmpGWcJfZ/u8imGnHSZm/bambootracker-0.4.5+git20121209/BambooTracker/resources/icon/iconfinder_arrow_up_4999.png and /tmp/tmpGWcJfZ/QnT7Jpno1T/bambootracker-0.4.6/BambooTracker/resources/icon/iconfinder_arrow_up_4999.png differ Binary files /tmp/tmpGWcJfZ/u8imGnHSZm/bambootracker-0.4.5+git20121209/BambooTracker/resources/icon/iconfinder_asterisk_yellow_5001.png and /tmp/tmpGWcJfZ/QnT7Jpno1T/bambootracker-0.4.6/BambooTracker/resources/icon/iconfinder_asterisk_yellow_5001.png differ Binary files /tmp/tmpGWcJfZ/u8imGnHSZm/bambootracker-0.4.5+git20121209/BambooTracker/resources/icon/iconfinder_chart_line_edit_5152.png and /tmp/tmpGWcJfZ/QnT7Jpno1T/bambootracker-0.4.6/BambooTracker/resources/icon/iconfinder_chart_line_edit_5152.png differ Binary files /tmp/tmpGWcJfZ/u8imGnHSZm/bambootracker-0.4.5+git20121209/BambooTracker/resources/icon/iconfinder_cross_5233.png and /tmp/tmpGWcJfZ/QnT7Jpno1T/bambootracker-0.4.6/BambooTracker/resources/icon/iconfinder_cross_5233.png differ Binary files /tmp/tmpGWcJfZ/u8imGnHSZm/bambootracker-0.4.5+git20121209/BambooTracker/resources/icon/iconfinder_eye_5333.png and /tmp/tmpGWcJfZ/QnT7Jpno1T/bambootracker-0.4.6/BambooTracker/resources/icon/iconfinder_eye_5333.png differ Binary files /tmp/tmpGWcJfZ/u8imGnHSZm/bambootracker-0.4.5+git20121209/BambooTracker/resources/icon/iconfinder_magifier_zoom_out_5483.png and /tmp/tmpGWcJfZ/QnT7Jpno1T/bambootracker-0.4.6/BambooTracker/resources/icon/iconfinder_magifier_zoom_out_5483.png differ Binary files /tmp/tmpGWcJfZ/u8imGnHSZm/bambootracker-0.4.5+git20121209/BambooTracker/resources/icon/iconfinder_magnifier_zoom_in_5485.png and /tmp/tmpGWcJfZ/QnT7Jpno1T/bambootracker-0.4.6/BambooTracker/resources/icon/iconfinder_magnifier_zoom_in_5485.png differ Binary files /tmp/tmpGWcJfZ/u8imGnHSZm/bambootracker-0.4.5+git20121209/BambooTracker/resources/icon/iconfinder_page_add_5548.png and /tmp/tmpGWcJfZ/QnT7Jpno1T/bambootracker-0.4.6/BambooTracker/resources/icon/iconfinder_page_add_5548.png differ Binary files /tmp/tmpGWcJfZ/u8imGnHSZm/bambootracker-0.4.5+git20121209/BambooTracker/resources/icon/iconfinder_page_copy_5551.png and /tmp/tmpGWcJfZ/QnT7Jpno1T/bambootracker-0.4.6/BambooTracker/resources/icon/iconfinder_page_copy_5551.png differ Binary files /tmp/tmpGWcJfZ/u8imGnHSZm/bambootracker-0.4.5+git20121209/BambooTracker/resources/icon/iconfinder_page_delete_5552.png and /tmp/tmpGWcJfZ/QnT7Jpno1T/bambootracker-0.4.6/BambooTracker/resources/icon/iconfinder_page_delete_5552.png differ Binary files /tmp/tmpGWcJfZ/u8imGnHSZm/bambootracker-0.4.5+git20121209/BambooTracker/resources/icon/iconfinder_page_white_get_5593.png and /tmp/tmpGWcJfZ/QnT7Jpno1T/bambootracker-0.4.6/BambooTracker/resources/icon/iconfinder_page_white_get_5593.png differ Binary files /tmp/tmpGWcJfZ/u8imGnHSZm/bambootracker-0.4.5+git20121209/BambooTracker/resources/icon/iconfinder_page_white_put_5609.png and /tmp/tmpGWcJfZ/QnT7Jpno1T/bambootracker-0.4.6/BambooTracker/resources/icon/iconfinder_page_white_put_5609.png differ Binary files /tmp/tmpGWcJfZ/u8imGnHSZm/bambootracker-0.4.5+git20121209/BambooTracker/resources/icon/iconfinder_pencil_5631.png and /tmp/tmpGWcJfZ/QnT7Jpno1T/bambootracker-0.4.6/BambooTracker/resources/icon/iconfinder_pencil_5631.png differ Binary files /tmp/tmpGWcJfZ/u8imGnHSZm/bambootracker-0.4.5+git20121209/BambooTracker/resources/icon/iconfinder_shape_flip_horizontal_5740.png and /tmp/tmpGWcJfZ/QnT7Jpno1T/bambootracker-0.4.6/BambooTracker/resources/icon/iconfinder_shape_flip_horizontal_5740.png differ Binary files /tmp/tmpGWcJfZ/u8imGnHSZm/bambootracker-0.4.5+git20121209/BambooTracker/resources/icon/iconfinder_stop_5785.png and /tmp/tmpGWcJfZ/QnT7Jpno1T/bambootracker-0.4.6/BambooTracker/resources/icon/iconfinder_stop_5785.png differ Binary files /tmp/tmpGWcJfZ/u8imGnHSZm/bambootracker-0.4.5+git20121209/BambooTracker/resources/icon/iconfinder_textfield_rename_5877.png and /tmp/tmpGWcJfZ/QnT7Jpno1T/bambootracker-0.4.6/BambooTracker/resources/icon/iconfinder_textfield_rename_5877.png differ Binary files /tmp/tmpGWcJfZ/u8imGnHSZm/bambootracker-0.4.5+git20121209/BambooTracker/resources/icon/iconfinder_wrench_orange_5931.png and /tmp/tmpGWcJfZ/QnT7Jpno1T/bambootracker-0.4.6/BambooTracker/resources/icon/iconfinder_wrench_orange_5931.png differ diff -Nru bambootracker-0.4.5+git20121209/BambooTracker/resources/icon/icon.qrc bambootracker-0.4.6/BambooTracker/resources/icon/icon.qrc --- bambootracker-0.4.5+git20121209/BambooTracker/resources/icon/icon.qrc 1970-01-01 00:00:00.000000000 +0000 +++ bambootracker-0.4.6/BambooTracker/resources/icon/icon.qrc 2021-02-11 14:37:17.000000000 +0000 @@ -0,0 +1,41 @@ + + + if_arrow_redo_4989.png + if_arrow_undo_4998.png + if_cog_5175.png + if_cut_red_5249.png + if_disk_5276.png + if_folder_5362.png + if_page_white_5568.png + if_page_white_copy_5579.png + if_paste_plain_5629.png + if_resultset_last_5687.png + if_resultset_next_5688.png + if_shape_square_5750.png + if_table_edit_5800.png + inst_ssg.png + inst_fm.png + BambooTracker.ico + iconfinder_add_4941.png + iconfinder_cross_5233.png + iconfinder_page_copy_5551.png + iconfinder_page_white_get_5593.png + iconfinder_page_white_put_5609.png + iconfinder_pencil_5631.png + iconfinder_stop_5785.png + iconfinder_arrow_down_4982.png + iconfinder_arrow_up_4999.png + iconfinder_asterisk_yellow_5001.png + iconfinder_page_add_5548.png + iconfinder_page_delete_5552.png + iconfinder_textfield_rename_5877.png + inst_adpcm.png + inst_kit.png + iconfinder_wrench_orange_5931.png + iconfinder_shape_flip_horizontal_5740.png + iconfinder_magifier_zoom_out_5483.png + iconfinder_magnifier_zoom_in_5485.png + iconfinder_eye_5333.png + iconfinder_chart_line_edit_5152.png + + Binary files /tmp/tmpGWcJfZ/u8imGnHSZm/bambootracker-0.4.5+git20121209/BambooTracker/resources/icon/if_arrow_redo_4989.png and /tmp/tmpGWcJfZ/QnT7Jpno1T/bambootracker-0.4.6/BambooTracker/resources/icon/if_arrow_redo_4989.png differ Binary files /tmp/tmpGWcJfZ/u8imGnHSZm/bambootracker-0.4.5+git20121209/BambooTracker/resources/icon/if_arrow_undo_4998.png and /tmp/tmpGWcJfZ/QnT7Jpno1T/bambootracker-0.4.6/BambooTracker/resources/icon/if_arrow_undo_4998.png differ Binary files /tmp/tmpGWcJfZ/u8imGnHSZm/bambootracker-0.4.5+git20121209/BambooTracker/resources/icon/if_cog_5175.png and /tmp/tmpGWcJfZ/QnT7Jpno1T/bambootracker-0.4.6/BambooTracker/resources/icon/if_cog_5175.png differ Binary files /tmp/tmpGWcJfZ/u8imGnHSZm/bambootracker-0.4.5+git20121209/BambooTracker/resources/icon/if_cut_red_5249.png and /tmp/tmpGWcJfZ/QnT7Jpno1T/bambootracker-0.4.6/BambooTracker/resources/icon/if_cut_red_5249.png differ Binary files /tmp/tmpGWcJfZ/u8imGnHSZm/bambootracker-0.4.5+git20121209/BambooTracker/resources/icon/if_disk_5276.png and /tmp/tmpGWcJfZ/QnT7Jpno1T/bambootracker-0.4.6/BambooTracker/resources/icon/if_disk_5276.png differ Binary files /tmp/tmpGWcJfZ/u8imGnHSZm/bambootracker-0.4.5+git20121209/BambooTracker/resources/icon/if_folder_5362.png and /tmp/tmpGWcJfZ/QnT7Jpno1T/bambootracker-0.4.6/BambooTracker/resources/icon/if_folder_5362.png differ Binary files /tmp/tmpGWcJfZ/u8imGnHSZm/bambootracker-0.4.5+git20121209/BambooTracker/resources/icon/if_page_white_5568.png and /tmp/tmpGWcJfZ/QnT7Jpno1T/bambootracker-0.4.6/BambooTracker/resources/icon/if_page_white_5568.png differ Binary files /tmp/tmpGWcJfZ/u8imGnHSZm/bambootracker-0.4.5+git20121209/BambooTracker/resources/icon/if_page_white_copy_5579.png and /tmp/tmpGWcJfZ/QnT7Jpno1T/bambootracker-0.4.6/BambooTracker/resources/icon/if_page_white_copy_5579.png differ Binary files /tmp/tmpGWcJfZ/u8imGnHSZm/bambootracker-0.4.5+git20121209/BambooTracker/resources/icon/if_paste_plain_5629.png and /tmp/tmpGWcJfZ/QnT7Jpno1T/bambootracker-0.4.6/BambooTracker/resources/icon/if_paste_plain_5629.png differ Binary files /tmp/tmpGWcJfZ/u8imGnHSZm/bambootracker-0.4.5+git20121209/BambooTracker/resources/icon/if_resultset_last_5687.png and /tmp/tmpGWcJfZ/QnT7Jpno1T/bambootracker-0.4.6/BambooTracker/resources/icon/if_resultset_last_5687.png differ Binary files /tmp/tmpGWcJfZ/u8imGnHSZm/bambootracker-0.4.5+git20121209/BambooTracker/resources/icon/if_resultset_next_5688.png and /tmp/tmpGWcJfZ/QnT7Jpno1T/bambootracker-0.4.6/BambooTracker/resources/icon/if_resultset_next_5688.png differ Binary files /tmp/tmpGWcJfZ/u8imGnHSZm/bambootracker-0.4.5+git20121209/BambooTracker/resources/icon/if_shape_square_5750.png and /tmp/tmpGWcJfZ/QnT7Jpno1T/bambootracker-0.4.6/BambooTracker/resources/icon/if_shape_square_5750.png differ Binary files /tmp/tmpGWcJfZ/u8imGnHSZm/bambootracker-0.4.5+git20121209/BambooTracker/resources/icon/if_table_edit_5800.png and /tmp/tmpGWcJfZ/QnT7Jpno1T/bambootracker-0.4.6/BambooTracker/resources/icon/if_table_edit_5800.png differ Binary files /tmp/tmpGWcJfZ/u8imGnHSZm/bambootracker-0.4.5+git20121209/BambooTracker/resources/icon/inst_adpcm.png and /tmp/tmpGWcJfZ/QnT7Jpno1T/bambootracker-0.4.6/BambooTracker/resources/icon/inst_adpcm.png differ Binary files /tmp/tmpGWcJfZ/u8imGnHSZm/bambootracker-0.4.5+git20121209/BambooTracker/resources/icon/inst_fm.png and /tmp/tmpGWcJfZ/QnT7Jpno1T/bambootracker-0.4.6/BambooTracker/resources/icon/inst_fm.png differ Binary files /tmp/tmpGWcJfZ/u8imGnHSZm/bambootracker-0.4.5+git20121209/BambooTracker/resources/icon/inst_kit.png and /tmp/tmpGWcJfZ/QnT7Jpno1T/bambootracker-0.4.6/BambooTracker/resources/icon/inst_kit.png differ Binary files /tmp/tmpGWcJfZ/u8imGnHSZm/bambootracker-0.4.5+git20121209/BambooTracker/resources/icon/inst_ssg.png and /tmp/tmpGWcJfZ/QnT7Jpno1T/bambootracker-0.4.6/BambooTracker/resources/icon/inst_ssg.png differ diff -Nru bambootracker-0.4.5+git20121209/BambooTracker/resources/resources.pri bambootracker-0.4.6/BambooTracker/resources/resources.pri --- bambootracker-0.4.5+git20121209/BambooTracker/resources/resources.pri 1970-01-01 00:00:00.000000000 +0000 +++ bambootracker-0.4.6/BambooTracker/resources/resources.pri 2021-02-11 14:37:17.000000000 +0000 @@ -0,0 +1,13 @@ +# QMake wrapper over compiled-in Qt Resource definitions + +RESOURCES += \ + $$PWD/doc/doc.qrc \ + $$PWD/icon/icon.qrc + +# Platform-specific app icon +win32 { + RC_ICONS = $$PWD/icon/BambooTracker.ico +} +else:osx { + ICON = $$PWD/icon/BambooTracker.icns +} diff -Nru bambootracker-0.4.5+git20121209/BambooTracker/song_length_calculator.cpp bambootracker-0.4.6/BambooTracker/song_length_calculator.cpp --- bambootracker-0.4.5+git20121209/BambooTracker/song_length_calculator.cpp 2020-12-08 14:05:28.000000000 +0000 +++ bambootracker-0.4.6/BambooTracker/song_length_calculator.cpp 2021-02-11 14:37:17.000000000 +0000 @@ -44,7 +44,7 @@ { } -double SongLengthCalculator::calculateBySecond() const +double SongLengthCalculator::approximateLengthBySecond() const { Song& song = mod_.getSong(songNum_); std::unordered_set visitedOrder; @@ -53,7 +53,7 @@ const int rate = static_cast(mod_.getTickFrequency()); int tempo = song.getTempo(); int speed = song.getSpeed(); - std::vector groove = mod_.getGroove(song.getGroove()).getSequence(); + std::vector groove = mod_.getGroove(song.getGroove()); double stepTicks = calculateStrictStepTicks(rate, tempo, speed); size_t grooveIdx = 0; bool isTempo = song.isUsedTempo(); @@ -73,8 +73,8 @@ // Load effects for (const TrackAttribute& attrib : attribs) { Step& step = song.getTrack(attrib.number).getPatternFromOrderNumber(orderNum).getStep(stepNum); - for (int e = 0; e < 4; ++e) { - const Effect&& eff = Effect::makeEffectData(attrib.source, step.getEffectID(e), step.getEffectValue(e)); + for (int e = 0; e < Step::N_EFFECT; ++e) { + const Effect&& eff = effect_utils::validateEffect(attrib.source, step.getEffect(e)); switch (eff.type) { case EffectType::SpeedTempoChange: case EffectType::Groove: @@ -109,7 +109,7 @@ switch (eff.first) { case EffectType::Groove: if (eff.second < static_cast(mod_.getGrooveCount())) { - groove = mod_.getGroove(eff.second).getSequence(); + groove = mod_.getGroove(eff.second); isTempo = false; grooveIdx = 0; } @@ -156,3 +156,58 @@ // Calculate time by seconds return tickCnt / rate; } + +void SongLengthCalculator::totalStepCount(size_t &introSize, size_t &loopSize) const +{ + Song& song = mod_.getSong(songNum_); + std::vector attribs = song.getTrackAttributes(); + size_t totalStepCnt = 0; + std::unordered_map stepCntLogMap; + int lastOrder = static_cast(song.getOrderSize()) - 1; + int curOrder = 0; + + for (int curStep = 0; !stepCntLogMap.count(curOrder); ) { + // Count up + size_t ptnFullSize = song.getPatternSizeFromOrderNumber(curOrder); + stepCntLogMap[curOrder] = totalStepCnt; + totalStepCnt += (ptnFullSize - curStep); + + // Check next order position + for (const auto& attrib : attribs) { + const Step& step = song.getTrack(attrib.number) + .getPatternFromOrderNumber(curOrder).getStep(ptnFullSize - 1); + for (int i = 0; i < Step::N_EFFECT; ++i) { + Effect&& eff = effect_utils::validateEffect(attrib.source, step.getEffect(i)); + switch (eff.type) { + case EffectType::PositionJump: + if (eff.value <= lastOrder) { + curOrder = eff.value; + curStep = 0; + } + break; + case EffectType::SongEnd: // No loop + introSize = totalStepCnt; + loopSize = 0; + return; + case EffectType::PatternBreak: + if (curOrder == lastOrder + && eff.value < static_cast(song.getPatternSizeFromOrderNumber(0))) { + curOrder = 0; + curStep = eff.value; + } + else if (eff.value < static_cast(song.getPatternSizeFromOrderNumber(curOrder + 1))) { + ++curOrder; + curStep = eff.value; + } + break; + default: + break; + } + } + } + } + + // Calculate counts + introSize = stepCntLogMap[curOrder]; + loopSize = totalStepCnt - introSize; +} diff -Nru bambootracker-0.4.5+git20121209/BambooTracker/song_length_calculator.hpp bambootracker-0.4.6/BambooTracker/song_length_calculator.hpp --- bambootracker-0.4.5+git20121209/BambooTracker/song_length_calculator.hpp 2020-12-08 14:05:28.000000000 +0000 +++ bambootracker-0.4.6/BambooTracker/song_length_calculator.hpp 2021-02-11 14:37:17.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (C) 2020 Rerrah + * Copyright (C) 2020-2021 Rerrah * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation @@ -25,13 +25,16 @@ #pragma once +#include + class Module; class SongLengthCalculator { public: SongLengthCalculator(Module& mod, int songNum); - double calculateBySecond() const; + double approximateLengthBySecond() const; + void totalStepCount(size_t& introSize, size_t& loopSize) const; private: Module& mod_; diff -Nru bambootracker-0.4.5+git20121209/BambooTracker/tick_counter.cpp bambootracker-0.4.6/BambooTracker/tick_counter.cpp --- bambootracker-0.4.5+git20121209/BambooTracker/tick_counter.cpp 2020-12-08 14:05:28.000000000 +0000 +++ bambootracker-0.4.6/BambooTracker/tick_counter.cpp 2021-02-11 14:37:17.000000000 +0000 @@ -67,7 +67,7 @@ return defStepSize_; } -void TickCounter::setGroove(std::vector seq) +void TickCounter::setGroove(const std::vector& seq) { grooves_ = seq; if (nextGroovePos_ != -1) nextGroovePos_ = 0; diff -Nru bambootracker-0.4.5+git20121209/BambooTracker/tick_counter.hpp bambootracker-0.4.6/BambooTracker/tick_counter.hpp --- bambootracker-0.4.5+git20121209/BambooTracker/tick_counter.hpp 2020-12-08 14:05:28.000000000 +0000 +++ bambootracker-0.4.6/BambooTracker/tick_counter.hpp 2021-02-11 14:37:17.000000000 +0000 @@ -45,7 +45,7 @@ int getTempo() const noexcept; void setSpeed(int speed); int getSpeed() const noexcept; - void setGroove(std::vector seq); + void setGroove(const std::vector& seq); void setGrooveState(GrooveState state); bool getGrooveEnabled() const noexcept; void setPlayState(bool isPlaySong) noexcept; diff -Nru bambootracker-0.4.5+git20121209/BambooTracker/utils.hpp bambootracker-0.4.6/BambooTracker/utils.hpp --- bambootracker-0.4.5+git20121209/BambooTracker/utils.hpp 1970-01-01 00:00:00.000000000 +0000 +++ bambootracker-0.4.6/BambooTracker/utils.hpp 2021-02-11 14:37:17.000000000 +0000 @@ -0,0 +1,112 @@ +/* + * Copyright (C) 2018-2021 Rerrah + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +#pragma once + +#include +#include +#include +#include + +namespace utils +{ +template +inline T clamp(T value, T low, T high) +{ + return std::min(std::max(value, low), high); +} + +template +inline auto find(InputContainer& input, const T& value) +{ + return std::find(input.begin(), input.end(), value); +} + +template +inline auto findIf(InputContainer& input, Predicate&& pred) +{ + return std::find_if(input.begin(), input.end(), std::forward(pred)); +} + +template , class InputIterator, class Predicate> +inline auto findIndicesIf(InputIterator first, InputIterator last, Predicate pred) +{ + using T = typename OutputContainer::value_type; + OutputContainer idcs; + for (auto it = first; (it = std::find_if(it, last, pred)) != last; ++it) { + idcs.push_back(static_cast(std::distance(first, it))); + } + return idcs; +} + +template , class InputContainer, class Predicate> +inline auto findIndicesIf(const InputContainer& input, Predicate&& pred) +{ + return findIndicesIf(input.begin(), input.end(), std::forward(pred)); +} + +template