libopenmpt_ext_impl.cpp 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352
  1. /*
  2. * libopenmpt_ext_impl.cpp
  3. * -----------------------
  4. * Purpose: libopenmpt extensions - implementation
  5. * Notes :
  6. * Authors: OpenMPT Devs
  7. * The OpenMPT source code is released under the BSD license. Read LICENSE for more details.
  8. */
  9. #include "common/stdafx.h"
  10. #include "libopenmpt_internal.h"
  11. #include "libopenmpt_ext.hpp"
  12. #include "libopenmpt_ext_impl.hpp"
  13. #include "mpt/base/saturate_round.hpp"
  14. #include "soundlib/Sndfile.h"
  15. // assume OPENMPT_NAMESPACE is OpenMPT
  16. namespace openmpt {
  17. module_ext_impl::module_ext_impl( callback_stream_wrapper stream, std::unique_ptr<log_interface> log, const std::map< std::string, std::string > & ctls ) : module_impl( stream, std::move(log), ctls ) {
  18. ctor();
  19. }
  20. module_ext_impl::module_ext_impl( std::istream & stream, std::unique_ptr<log_interface> log, const std::map< std::string, std::string > & ctls ) : module_impl( stream, std::move(log), ctls ) {
  21. ctor();
  22. }
  23. module_ext_impl::module_ext_impl( const std::vector<std::byte> & data, std::unique_ptr<log_interface> log, const std::map< std::string, std::string > & ctls ) : module_impl( data, std::move(log), ctls ) {
  24. ctor();
  25. }
  26. module_ext_impl::module_ext_impl( const std::vector<std::uint8_t> & data, std::unique_ptr<log_interface> log, const std::map< std::string, std::string > & ctls ) : module_impl( data, std::move(log), ctls ) {
  27. ctor();
  28. }
  29. module_ext_impl::module_ext_impl( const std::vector<char> & data, std::unique_ptr<log_interface> log, const std::map< std::string, std::string > & ctls ) : module_impl( data, std::move(log), ctls ) {
  30. ctor();
  31. }
  32. module_ext_impl::module_ext_impl( const std::byte * data, std::size_t size, std::unique_ptr<log_interface> log, const std::map< std::string, std::string > & ctls ) : module_impl( data, size, std::move(log), ctls ) {
  33. ctor();
  34. }
  35. module_ext_impl::module_ext_impl( const std::uint8_t * data, std::size_t size, std::unique_ptr<log_interface> log, const std::map< std::string, std::string > & ctls ) : module_impl( data, size, std::move(log), ctls ) {
  36. ctor();
  37. }
  38. module_ext_impl::module_ext_impl( const char * data, std::size_t size, std::unique_ptr<log_interface> log, const std::map< std::string, std::string > & ctls ) : module_impl( data, size, std::move(log), ctls ) {
  39. ctor();
  40. }
  41. module_ext_impl::module_ext_impl( const void * data, std::size_t size, std::unique_ptr<log_interface> log, const std::map< std::string, std::string > & ctls ) : module_impl( data, size, std::move(log), ctls ) {
  42. ctor();
  43. }
  44. void module_ext_impl::ctor() {
  45. /* add stuff here */
  46. }
  47. module_ext_impl::~module_ext_impl() {
  48. /* add stuff here */
  49. }
  50. void * module_ext_impl::get_interface( const std::string & interface_id ) {
  51. if ( interface_id.empty() ) {
  52. return 0;
  53. } else if ( interface_id == ext::pattern_vis_id ) {
  54. return dynamic_cast< ext::pattern_vis * >( this );
  55. } else if ( interface_id == ext::interactive_id ) {
  56. return dynamic_cast< ext::interactive * >( this );
  57. } else if ( interface_id == ext::interactive2_id ) {
  58. return dynamic_cast< ext::interactive2 * >( this );
  59. /* add stuff here */
  60. } else {
  61. return 0;
  62. }
  63. }
  64. // pattern_vis
  65. module_ext_impl::effect_type module_ext_impl::get_pattern_row_channel_volume_effect_type( std::int32_t pattern, std::int32_t row, std::int32_t channel ) const {
  66. std::uint8_t byte = get_pattern_row_channel_command( pattern, row, channel, module::command_volumeffect );
  67. switch ( OpenMPT::ModCommand::GetVolumeEffectType( byte ) ) {
  68. case OpenMPT::EFFECT_TYPE_NORMAL : return effect_general; break;
  69. case OpenMPT::EFFECT_TYPE_GLOBAL : return effect_global ; break;
  70. case OpenMPT::EFFECT_TYPE_VOLUME : return effect_volume ; break;
  71. case OpenMPT::EFFECT_TYPE_PANNING: return effect_panning; break;
  72. case OpenMPT::EFFECT_TYPE_PITCH : return effect_pitch ; break;
  73. default: return effect_unknown; break;
  74. }
  75. }
  76. module_ext_impl::effect_type module_ext_impl::get_pattern_row_channel_effect_type( std::int32_t pattern, std::int32_t row, std::int32_t channel ) const {
  77. std::uint8_t byte = get_pattern_row_channel_command( pattern, row, channel, module::command_effect );
  78. switch (OpenMPT::ModCommand::GetEffectType( byte ) ) {
  79. case OpenMPT::EFFECT_TYPE_NORMAL : return effect_general; break;
  80. case OpenMPT::EFFECT_TYPE_GLOBAL : return effect_global ; break;
  81. case OpenMPT::EFFECT_TYPE_VOLUME : return effect_volume ; break;
  82. case OpenMPT::EFFECT_TYPE_PANNING: return effect_panning; break;
  83. case OpenMPT::EFFECT_TYPE_PITCH : return effect_pitch ; break;
  84. default: return effect_unknown; break;
  85. }
  86. }
  87. // interactive
  88. void module_ext_impl::set_current_speed( std::int32_t speed ) {
  89. if ( speed < 1 || speed > 65535 ) {
  90. throw openmpt::exception("invalid tick count");
  91. }
  92. m_sndFile->m_PlayState.m_nMusicSpeed = speed;
  93. }
  94. void module_ext_impl::set_current_tempo( std::int32_t tempo ) {
  95. if ( tempo < 32 || tempo > 512 ) {
  96. throw openmpt::exception("invalid tempo");
  97. }
  98. m_sndFile->m_PlayState.m_nMusicTempo.Set( tempo );
  99. }
  100. void module_ext_impl::set_tempo_factor( double factor ) {
  101. if ( factor <= 0.0 || factor > 4.0 ) {
  102. throw openmpt::exception("invalid tempo factor");
  103. }
  104. m_sndFile->m_nTempoFactor = mpt::saturate_round<uint32_t>( 65536.0 / factor );
  105. m_sndFile->RecalculateSamplesPerTick();
  106. }
  107. double module_ext_impl::get_tempo_factor( ) const {
  108. return 65536.0 / m_sndFile->m_nTempoFactor;
  109. }
  110. void module_ext_impl::set_pitch_factor( double factor ) {
  111. if ( factor <= 0.0 || factor > 4.0 ) {
  112. throw openmpt::exception("invalid pitch factor");
  113. }
  114. m_sndFile->m_nFreqFactor = mpt::saturate_round<uint32_t>( 65536.0 * factor );
  115. m_sndFile->RecalculateSamplesPerTick();
  116. }
  117. double module_ext_impl::get_pitch_factor( ) const {
  118. return m_sndFile->m_nFreqFactor / 65536.0;
  119. }
  120. void module_ext_impl::set_global_volume( double volume ) {
  121. if ( volume < 0.0 || volume > 1.0 ) {
  122. throw openmpt::exception("invalid global volume");
  123. }
  124. m_sndFile->m_PlayState.m_nGlobalVolume = mpt::saturate_round<uint32_t>( volume * MAX_GLOBAL_VOLUME );
  125. }
  126. double module_ext_impl::get_global_volume( ) const {
  127. return m_sndFile->m_PlayState.m_nGlobalVolume / static_cast<double>( MAX_GLOBAL_VOLUME );
  128. }
  129. void module_ext_impl::set_channel_volume( std::int32_t channel, double volume ) {
  130. if ( channel < 0 || channel >= get_num_channels() ) {
  131. throw openmpt::exception("invalid channel");
  132. }
  133. if ( volume < 0.0 || volume > 1.0 ) {
  134. throw openmpt::exception("invalid global volume");
  135. }
  136. m_sndFile->m_PlayState.Chn[channel].nGlobalVol = mpt::saturate_round<std::int32_t>(volume * 64.0);
  137. }
  138. double module_ext_impl::get_channel_volume( std::int32_t channel ) const {
  139. if ( channel < 0 || channel >= get_num_channels() ) {
  140. throw openmpt::exception("invalid channel");
  141. }
  142. return m_sndFile->m_PlayState.Chn[channel].nGlobalVol / 64.0;
  143. }
  144. void module_ext_impl::set_channel_mute_status( std::int32_t channel, bool mute ) {
  145. if ( channel < 0 || channel >= get_num_channels() ) {
  146. throw openmpt::exception("invalid channel");
  147. }
  148. m_sndFile->ChnSettings[channel].dwFlags.set( OpenMPT::CHN_MUTE | OpenMPT::CHN_SYNCMUTE , mute );
  149. m_sndFile->m_PlayState.Chn[channel].dwFlags.set( OpenMPT::CHN_MUTE | OpenMPT::CHN_SYNCMUTE , mute );
  150. // Also update NNA channels
  151. for ( OpenMPT::CHANNELINDEX i = m_sndFile->GetNumChannels(); i < OpenMPT::MAX_CHANNELS; i++)
  152. {
  153. if ( m_sndFile->m_PlayState.Chn[i].nMasterChn == channel + 1)
  154. {
  155. m_sndFile->m_PlayState.Chn[i].dwFlags.set( OpenMPT::CHN_MUTE | OpenMPT::CHN_SYNCMUTE, mute );
  156. }
  157. }
  158. }
  159. bool module_ext_impl::get_channel_mute_status( std::int32_t channel ) const {
  160. if ( channel < 0 || channel >= get_num_channels() ) {
  161. throw openmpt::exception("invalid channel");
  162. }
  163. return m_sndFile->m_PlayState.Chn[channel].dwFlags[OpenMPT::CHN_MUTE | OpenMPT::CHN_SYNCMUTE];
  164. }
  165. void module_ext_impl::set_instrument_mute_status( std::int32_t instrument, bool mute ) {
  166. const bool instrument_mode = get_num_instruments() != 0;
  167. const std::int32_t max_instrument = instrument_mode ? get_num_instruments() : get_num_samples();
  168. if ( instrument < 0 || instrument >= max_instrument ) {
  169. throw openmpt::exception("invalid instrument");
  170. }
  171. if ( instrument_mode ) {
  172. if ( m_sndFile->Instruments[instrument + 1] != nullptr ) {
  173. m_sndFile->Instruments[instrument + 1]->dwFlags.set( OpenMPT::INS_MUTE, mute );
  174. }
  175. } else {
  176. m_sndFile->GetSample( static_cast<OpenMPT::SAMPLEINDEX>( instrument + 1 ) ).uFlags.set( OpenMPT::CHN_MUTE, mute ) ;
  177. }
  178. }
  179. bool module_ext_impl::get_instrument_mute_status( std::int32_t instrument ) const {
  180. const bool instrument_mode = get_num_instruments() != 0;
  181. const std::int32_t max_instrument = instrument_mode ? get_num_instruments() : get_num_samples();
  182. if ( instrument < 0 || instrument >= max_instrument ) {
  183. throw openmpt::exception("invalid instrument");
  184. }
  185. if ( instrument_mode ) {
  186. if ( m_sndFile->Instruments[instrument + 1] != nullptr ) {
  187. return m_sndFile->Instruments[instrument + 1]->dwFlags[OpenMPT::INS_MUTE];
  188. }
  189. return true;
  190. } else {
  191. return m_sndFile->GetSample( static_cast<OpenMPT::SAMPLEINDEX>( instrument + 1 ) ).uFlags[OpenMPT::CHN_MUTE];
  192. }
  193. }
  194. std::int32_t module_ext_impl::play_note( std::int32_t instrument, std::int32_t note, double volume, double panning ) {
  195. const bool instrument_mode = get_num_instruments() != 0;
  196. const std::int32_t max_instrument = instrument_mode ? get_num_instruments() : get_num_samples();
  197. if ( instrument < 0 || instrument >= max_instrument ) {
  198. throw openmpt::exception("invalid instrument");
  199. }
  200. note += OpenMPT::NOTE_MIN;
  201. if ( note < OpenMPT::NOTE_MIN || note > OpenMPT::NOTE_MAX ) {
  202. throw openmpt::exception("invalid note");
  203. }
  204. // Find a free channel
  205. OpenMPT::CHANNELINDEX free_channel = m_sndFile->GetNNAChannel( OpenMPT::CHANNELINDEX_INVALID );
  206. if ( free_channel == OpenMPT::CHANNELINDEX_INVALID ) {
  207. free_channel = OpenMPT::MAX_CHANNELS - 1;
  208. }
  209. OpenMPT::ModChannel &chn = m_sndFile->m_PlayState.Chn[free_channel];
  210. chn.Reset( OpenMPT::ModChannel::resetTotal, *m_sndFile, OpenMPT::CHANNELINDEX_INVALID, OpenMPT::CHN_MUTE );
  211. chn.nMasterChn = 0; // remove NNA association
  212. chn.nNewNote = chn.nLastNote = static_cast<std::uint8_t>(note);
  213. chn.ResetEnvelopes();
  214. m_sndFile->InstrumentChange(chn, instrument + 1);
  215. chn.nFadeOutVol = 0x10000;
  216. m_sndFile->NoteChange(chn, note, false, true, true);
  217. chn.nPan = mpt::saturate_round<std::int32_t>( OpenMPT::Clamp( panning * 128.0, -128.0, 128.0 ) + 128.0 );
  218. chn.nVolume = mpt::saturate_round<std::int32_t>( OpenMPT::Clamp( volume * 256.0, 0.0, 256.0 ) );
  219. // Remove channel from list of mixed channels to fix https://bugs.openmpt.org/view.php?id=209
  220. // This is required because a previous note on the same channel might have just stopped playing,
  221. // but the channel is still in the mix list.
  222. // Since the channel volume / etc is only updated every tick in CSoundFile::ReadNote, and we
  223. // do not want to duplicate mixmode-dependant logic here, CSoundFile::CreateStereoMix may already
  224. // try to mix our newly set up channel at volume 0 if we don't remove it from the list.
  225. auto mix_begin = std::begin( m_sndFile->m_PlayState.ChnMix );
  226. auto mix_end = std::remove( mix_begin, mix_begin + m_sndFile->m_nMixChannels, free_channel );
  227. m_sndFile->m_nMixChannels = static_cast<OpenMPT::CHANNELINDEX>( std::distance( mix_begin, mix_end ) );
  228. return free_channel;
  229. }
  230. void module_ext_impl::stop_note( std::int32_t channel ) {
  231. if ( channel < 0 || channel >= OpenMPT::MAX_CHANNELS ) {
  232. throw openmpt::exception("invalid channel");
  233. }
  234. auto & chn = m_sndFile->m_PlayState.Chn[channel];
  235. chn.nLength = 0;
  236. chn.pCurrentSample = nullptr;
  237. }
  238. void module_ext_impl::note_off(int32_t channel ) {
  239. if ( channel < 0 || channel >= OpenMPT::MAX_CHANNELS ) {
  240. throw openmpt::exception( "invalid channel" );
  241. }
  242. auto & chn = m_sndFile->m_PlayState.Chn[channel];
  243. chn.dwFlags |= OpenMPT::CHN_KEYOFF;
  244. }
  245. void module_ext_impl::note_fade(int32_t channel ) {
  246. if ( channel < 0 || channel >= OpenMPT::MAX_CHANNELS ) {
  247. throw openmpt::exception( "invalid channel" );
  248. }
  249. auto & chn = m_sndFile->m_PlayState.Chn[channel];
  250. chn.dwFlags |= OpenMPT::CHN_NOTEFADE;
  251. }
  252. void module_ext_impl::set_channel_panning( int32_t channel, double panning ) {
  253. if ( channel < 0 || channel >= OpenMPT::MAX_CHANNELS ) {
  254. throw openmpt::exception( "invalid channel" );
  255. }
  256. auto & chn = m_sndFile->m_PlayState.Chn[channel];
  257. chn.nPan = mpt::saturate_round<int32_t>( std::clamp( panning, -1.0, 1.0 ) * 128.0 + 128.0 );
  258. }
  259. double module_ext_impl::get_channel_panning( int32_t channel ) {
  260. if ( channel < 0 || channel >= OpenMPT::MAX_CHANNELS ) {
  261. throw openmpt::exception( "invalid channel" );
  262. }
  263. auto & chn = m_sndFile->m_PlayState.Chn[channel];
  264. return ( chn.nPan - 128 ) / 128.0;
  265. }
  266. void module_ext_impl::set_note_finetune( int32_t channel, double finetune ) {
  267. if ( channel < 0 || channel >= OpenMPT::MAX_CHANNELS ) {
  268. throw openmpt::exception( "invalid channel" );
  269. }
  270. auto & chn = m_sndFile->m_PlayState.Chn[channel];
  271. chn.microTuning = mpt::saturate_round<int16_t>( finetune * 32768.0 );
  272. }
  273. double module_ext_impl::get_note_finetune( int32_t channel ) {
  274. if ( channel < 0 || channel >= OpenMPT::MAX_CHANNELS ) {
  275. throw openmpt::exception( "invalid channel" );
  276. }
  277. auto & chn = m_sndFile->m_PlayState.Chn[channel];
  278. return chn.microTuning / 32768.0;
  279. }
  280. /* add stuff here */
  281. } // namespace openmpt