/* * Copyright (C) 2005-2018 Team Kodi * This file is part of Kodi - https://kodi.tv * * SPDX-License-Identifier: GPL-2.0-or-later * See LICENSES/README.md for more information. */ #pragma once /* * Parts with a comment named "internal" are only used inside header and not * used or accessed direct during add-on development! */ #include "../AddonBase.h" #include "../gui/renderHelper.h" namespace kodi { namespace addon { class CInstanceVisualization; }} extern "C" { struct AddonInstance_Visualization; typedef enum VIS_ACTION : unsigned int /* internal */ { VIS_ACTION_NONE = 0, VIS_ACTION_NEXT_PRESET, VIS_ACTION_PREV_PRESET, VIS_ACTION_LOAD_PRESET, VIS_ACTION_RANDOM_PRESET, VIS_ACTION_LOCK_PRESET, VIS_ACTION_RATE_PRESET_PLUS, VIS_ACTION_RATE_PRESET_MINUS, VIS_ACTION_UPDATE_ALBUMART, VIS_ACTION_UPDATE_TRACK } VIS_ACTION; struct VIS_INFO /* internal */ { bool bWantsFreq; int iSyncDelay; }; typedef struct AddonProps_Visualization /* internal */ { void *device; int x; int y; int width; int height; float pixelRatio; const char *name; const char *presets; const char *profile; } AddonProps_Visualization; typedef struct AddonToKodiFuncTable_Visualization /* internal */ { KODI_HANDLE kodiInstance; void (__cdecl* transfer_preset) (void* kodiInstance, const char* preset); void (__cdecl* clear_presets) (void* kodiInstance); } AddonToKodiFuncTable_Visualization; typedef struct KodiToAddonFuncTable_Visualization /* internal */ { kodi::addon::CInstanceVisualization* addonInstance; bool (__cdecl* start)(const AddonInstance_Visualization* instance, int channels, int samples_per_sec, int bits_per_sample, const char* song_name); void (__cdecl* stop)(const AddonInstance_Visualization* instance); void (__cdecl* audio_data)(const AddonInstance_Visualization* instance, const float* audio_data, int audio_data_length, float *freq_data, int freq_data_length); bool (__cdecl* is_dirty)(const AddonInstance_Visualization* instance); void (__cdecl* render)(const AddonInstance_Visualization* instance); void (__cdecl* get_info)(const AddonInstance_Visualization* instance, VIS_INFO *info); bool (__cdecl* on_action)(const AddonInstance_Visualization* instance, VIS_ACTION action, const void *param); unsigned int (__cdecl *get_presets)(const AddonInstance_Visualization* instance); int (__cdecl *get_active_preset)(const AddonInstance_Visualization* instance); bool (__cdecl* is_locked)(const AddonInstance_Visualization* instance); } KodiToAddonFuncTable_Visualization; typedef struct AddonInstance_Visualization /* internal */ { AddonProps_Visualization* props; AddonToKodiFuncTable_Visualization* toKodi; KodiToAddonFuncTable_Visualization* toAddon; } AddonInstance_Visualization; //============================================================================ /// \defgroup cpp_kodi_addon_visualization_VisTrack class VisTrack /// \ingroup cpp_kodi_addon_visualization /// @brief **Visualization track information structure** /// /// Called from kodi::addon::CInstanceVisualization::UpdateTrack() with the /// information of the currently-playing song. /// //@{ struct VisTrack { /// @brief Title of the current song. const char *title; /// @brief Artist names, as a single string const char *artist; /// @brief Album that the current song is from. const char *album; /// @brief Album artist names, as a single string const char *albumArtist; /// @brief The genre name from the music tag, if present. const char *genre; /// @brief Comment of the current song stored in the ID tag info. const char *comment; /// @brief Lyrics of the current song, if available. const char *lyrics; const char *reserved1; const char *reserved2; /// @brief Track number of the current song. int trackNumber; /// @brief Disc number of the current song stored in the ID tag info. int discNumber; /// @brief Duration of the current song, in seconds. int duration; /// @brief Year that the current song was released. int year; /// @brief The user-defined rating of the current song. int rating; int reserved3; int reserved4; }; //@} //---------------------------------------------------------------------------- } /* extern "C" */ namespace kodi { namespace addon { //============================================================================ /// /// \addtogroup cpp_kodi_addon_visualization /// @brief \cpp_class{ kodi::addon::CInstanceVisualization } /// **Visualization add-on instance** /// /// [Music visualization](https://en.wikipedia.org/wiki/Music_visualization), /// or music visualisation, is a feature in Kodi that generates animated /// imagery based on a piece of music. The imagery is usually generated and /// rendered in real time synchronized to the music. /// /// Visualization techniques range from simple ones (e.g., a simulation of an /// oscilloscope display) to elaborate ones, which often include a plurality /// of composited effects. The changes in the music's loudness and frequency /// spectrum are among the properties used as input to the visualization. /// /// Include the header \ref Visualization.h "#include " /// to use this class. /// /// This interface allows the creation of visualizations for Kodi, based upon /// **DirectX** or/and **OpenGL** rendering with `C++` code. /// /// Additionally, there are several \ref cpp_kodi_addon_visualization_CB "other functions" /// available in which the child class can ask about the current hardware, /// including the device, display and several other parts. /// /// -------------------------------------------------------------------------- /// /// /// **Here is an example of the minimum required code to start a visualization:** /// ~~~~~~~~~~~~~{.cpp} /// #include /// /// class CMyVisualization : public kodi::addon::CAddonBase, /// public kodi::addon::CInstanceVisualization /// { /// public: /// CMyVisualization(); /// /// bool Start(int channels, int samplesPerSec, int bitsPerSample, std::string songName) override; /// void AudioData(const float* audioData, int audioDataLength, float* freqData, int freqDataLength) override; /// void Render() override; /// }; /// /// CMyVisualization::CMyVisualization() /// { /// ... /// } /// /// bool CMyVisualization::Start(int channels, int samplesPerSec, int bitsPerSample, std::string songName) /// { /// ... /// return true; /// } /// /// void CMyVisualization::AudioData(const float* audioData, int audioDataLength, float* freqData, int freqDataLength) /// { /// ... /// } /// /// void CMyVisualization::Render() /// { /// ... /// } /// /// ADDONCREATOR(CMyVisualization) /// ~~~~~~~~~~~~~ /// /// /// -------------------------------------------------------------------------- /// /// /// **Here is another example where the visualization is used together with /// other instance types:** /// /// ~~~~~~~~~~~~~{.cpp} /// #include /// /// class CMyVisualization : public ::kodi::addon::CInstanceVisualization /// { /// public: /// CMyVisualization(KODI_HANDLE instance); /// /// bool Start(int channels, int samplesPerSec, int bitsPerSample, std::string songName) override; /// void AudioData(const float* audioData, int audioDataLength, float* freqData, int freqDataLength) override; /// void Render() override; /// }; /// /// CMyVisualization::CMyVisualization(KODI_HANDLE instance) /// : CInstanceVisualization(instance) /// { /// ... /// } /// /// bool CMyVisualization::Start(int channels, int samplesPerSec, int bitsPerSample, std::string songName) /// { /// ... /// return true; /// } /// /// void CMyVisualization::AudioData(const float* audioData, int audioDataLength, float* freqData, int freqDataLength) /// { /// ... /// } /// /// void CMyVisualization::Render() /// { /// ... /// } /// /// /// /*----------------------------------------------------------------------*/ /// /// class CMyAddon : public ::kodi::addon::CAddonBase /// { /// public: /// CMyAddon() { } /// ADDON_STATUS CreateInstance(int instanceType, /// std::string instanceID, /// KODI_HANDLE instance, /// KODI_HANDLE& addonInstance) override; /// }; /// /// /* If you use only one instance in your add-on, can be instanceType and /// * instanceID ignored */ /// ADDON_STATUS CMyAddon::CreateInstance(int instanceType, /// std::string instanceID, /// KODI_HANDLE instance, /// KODI_HANDLE& addonInstance) /// { /// if (instanceType == ADDON_INSTANCE_VISUALIZATION) /// { /// kodi::Log(ADDON_LOG_NOTICE, "Creating my Visualization"); /// addonInstance = new CMyVisualization(instance); /// return ADDON_STATUS_OK; /// } /// else if (...) /// { /// ... /// } /// return ADDON_STATUS_UNKNOWN; /// } /// /// ADDONCREATOR(CMyAddon) /// ~~~~~~~~~~~~~ /// /// The destruction of the example class `CMyVisualization` is called from /// Kodi's header. Manually deleting the add-on instance is not required. /// //---------------------------------------------------------------------------- class ATTRIBUTE_HIDDEN CInstanceVisualization : public IAddonInstance { public: //========================================================================== /// /// @ingroup cpp_kodi_addon_visualization /// @brief Visualization class constructor /// /// Used by an add-on that only supports visualizations. /// CInstanceVisualization() : IAddonInstance(ADDON_INSTANCE_VISUALIZATION, GetKodiTypeVersion(ADDON_INSTANCE_VISUALIZATION)) { if (CAddonBase::m_interface->globalSingleInstance != nullptr) throw std::logic_error("kodi::addon::CInstanceVisualization: Cannot create multiple instances of add-on."); SetAddonStruct(CAddonBase::m_interface->firstKodiInstance); CAddonBase::m_interface->globalSingleInstance = this; } //-------------------------------------------------------------------------- //========================================================================== /// /// @ingroup cpp_kodi_addon_visualization /// @brief Visualization class constructor used to support multiple instance /// types /// /// @param[in] instance The instance value given to /// `kodi::addon::CAddonBase::CreateInstance(...)`. /// @param[in] kodiVersion [opt] Version used in Kodi for this instance, to /// allow compatibility to older Kodi versions. /// @note Recommended to set. /// /// @warning Only use `instance` from the CreateInstance call /// explicit CInstanceVisualization(KODI_HANDLE instance, const std::string& kodiVersion = "") : IAddonInstance(ADDON_INSTANCE_VISUALIZATION, !kodiVersion.empty() ? kodiVersion : GetKodiTypeVersion(ADDON_INSTANCE_VISUALIZATION)) { if (CAddonBase::m_interface->globalSingleInstance != nullptr) throw std::logic_error("kodi::addon::CInstanceVisualization: Creation of multiple together with single instance way is not allowed!"); SetAddonStruct(instance); } //-------------------------------------------------------------------------- //========================================================================== /// /// @ingroup cpp_kodi_addon_visualization /// @brief Destructor /// ~CInstanceVisualization() override = default; //-------------------------------------------------------------------------- //========================================================================== /// /// @ingroup cpp_kodi_addon_visualization /// @brief Used to notify the visualization that a new song has been started /// /// @param[in] channels Number of channels in the stream /// @param[in] samplesPerSec Samples per second of stream /// @param[in] bitsPerSample Number of bits in one sample /// @param[in] songName The name of the currently-playing song /// @return true if start successful done /// virtual bool Start(int channels, int samplesPerSec, int bitsPerSample, std::string songName) { return true; } //-------------------------------------------------------------------------- //========================================================================== /// /// @ingroup cpp_kodi_addon_visualization /// @brief Used to inform the visualization that the rendering control was /// stopped /// virtual void Stop() {} //-------------------------------------------------------------------------- //========================================================================== /// /// @ingroup cpp_kodi_addon_visualization /// @brief Pass audio data to the visualization /// /// @param[in] audioData The raw audio data /// @param[in] audioDataLength Length of the audioData array /// @param[in] freqData The [FFT](https://en.wikipedia.org/wiki/Fast_Fourier_transform) /// of the audio data /// @param[in] freqDataLength Length of frequency data array /// /// Values **freqData** and **freqDataLength** are used if GetInfo() returns /// true for the `wantsFreq` parameter. Otherwise, **freqData** is set to /// `nullptr` and **freqDataLength** is `0`. /// virtual void AudioData(const float* audioData, int audioDataLength, float* freqData, int freqDataLength) {} //-------------------------------------------------------------------------- //========================================================================== /// /// @ingroup cpp_kodi_addon_visualization /// @brief Used to inform Kodi that the rendered region is dirty and need an /// update /// /// @return True if dirty /// virtual bool IsDirty() { return true; } //-------------------------------------------------------------------------- //========================================================================== /// /// @ingroup cpp_kodi_addon_visualization /// @brief Used to indicate when the add-on should render /// virtual void Render() {} //-------------------------------------------------------------------------- //========================================================================== /// /// @ingroup cpp_kodi_addon_visualization /// @brief Used to get the number of buffers from the current visualization /// /// @param[out] wantsFreq Indicates whether the add-on wants FFT /// data. If set to true, the **freqData** /// and **freqDataLength** parameters of /// AudioData() are used /// @param[out] syncDelay The number of buffers to delay before /// calling AudioData() /// /// @note If this function is not implemented, it will default to /// `wantsFreq` = false and `syncDelay` = 0. /// virtual void GetInfo(bool& wantsFreq, int& syncDelay) { wantsFreq = false; syncDelay = 0; } //-------------------------------------------------------------------------- //========================================================================== /// /// @ingroup cpp_kodi_addon_visualization /// @brief Used to get a list of visualization presets the user can select /// from /// /// @param[out] presets The vector list containing the names of /// presets that the user can select /// @return Return true if successful, or false if /// there are no presets to choose from /// virtual bool GetPresets(std::vector& presets) { return false; } //-------------------------------------------------------------------------- //========================================================================== /// /// @ingroup cpp_kodi_addon_visualization /// @brief Get the index of the current preset /// /// @return Index number of the current preset /// virtual int GetActivePreset() { return -1; } //-------------------------------------------------------------------------- //========================================================================== /// /// @ingroup cpp_kodi_addon_visualization /// @brief Check if the add-on is locked to the current preset /// /// @return True if locked to the current preset /// virtual bool IsLocked() { return false; } //-------------------------------------------------------------------------- //========================================================================== /// /// @ingroup cpp_kodi_addon_visualization /// @brief Load the previous visualization preset /// /// @return Return true if the previous preset was loaded virtual bool PrevPreset() { return false; } //-------------------------------------------------------------------------- //========================================================================== /// /// @ingroup cpp_kodi_addon_visualization /// @brief Load the next visualization preset /// /// @return Return true if the next preset was loaded virtual bool NextPreset() { return false; } //-------------------------------------------------------------------------- //========================================================================== /// /// @ingroup cpp_kodi_addon_visualization /// @brief Load a visualization preset /// /// This function is called after a new preset is selected. /// /// @param[in] select Preset index to use /// @return Return true if the preset is loaded virtual bool LoadPreset(int select) { return false; } //-------------------------------------------------------------------------- //========================================================================== /// /// @ingroup cpp_kodi_addon_visualization /// @brief Switch to a new random preset /// /// @return Return true if a random preset was loaded virtual bool RandomPreset() { return false; } //-------------------------------------------------------------------------- //========================================================================== /// /// @ingroup cpp_kodi_addon_visualization /// @brief Lock the current visualization preset, preventing it from changing /// /// @param[in] lockUnlock If set to true, the preset should be locked /// @return Return true if the current preset is locked virtual bool LockPreset(bool lockUnlock) { return false; } //-------------------------------------------------------------------------- //========================================================================== /// /// @ingroup cpp_kodi_addon_visualization /// @brief Used to increase/decrease the visualization preset rating /// /// @param[in] plusMinus If set to true the rating is increased, otherwise /// decreased /// @return Return true if the rating is modified virtual bool RatePreset(bool plusMinus) { return false; } //-------------------------------------------------------------------------- //========================================================================== /// /// @ingroup cpp_kodi_addon_visualization /// @brief Inform the visualization of the current album art image /// /// @param[in] albumart Path to the current album art image /// @return Return true if the image is used virtual bool UpdateAlbumart(std::string albumart) { return false; } //-------------------------------------------------------------------------- //========================================================================== /// /// @ingroup cpp_kodi_addon_visualization /// @brief Inform the visualization of the current track's tag information /// /// @param[in] track Visualization track information structure /// @return Return true if the track information is used virtual bool UpdateTrack(const VisTrack &track) { return false; } //========================================================================== /// /// \defgroup cpp_kodi_addon_visualization_CB Information functions /// \ingroup cpp_kodi_addon_visualization /// @brief **To get info about the device, display and several other parts** /// //@{ //========================================================================== /// /// @ingroup cpp_kodi_addon_visualization_CB /// @brief To transfer available presets on addon /// /// Used if @ref GetPresets not possible to use, e.g. where available presets /// are only known during @ref Start call. /// /// @param[in] presets List to store available presets. /// /// @note The function should only be called once, if possible /// inline void TransferPresets(const std::vector& presets) { m_instanceData->toKodi->clear_presets(m_instanceData->toKodi->kodiInstance); for (auto it : presets) m_instanceData->toKodi->transfer_preset(m_instanceData->toKodi->kodiInstance, it.c_str()); } //-------------------------------------------------------------------------- //========================================================================== /// /// @ingroup cpp_kodi_addon_visualization_CB /// @brief Device that represents the display adapter /// /// @return A pointer to the used device /// /// @note This is only available on **DirectX**, It us unused (`nullptr`) on /// **OpenGL** /// inline void* Device() { return m_instanceData->props->device; } //-------------------------------------------------------------------------- //========================================================================== /// /// @ingroup cpp_kodi_addon_visualization_CB /// @brief Returns the X position of the rendering window /// /// @return The X position, in pixels /// inline int X() { return m_instanceData->props->x; } //-------------------------------------------------------------------------- //========================================================================== /// /// @ingroup cpp_kodi_addon_visualization_CB /// @brief Returns the Y position of the rendering window /// /// @return The Y position, in pixels /// inline int Y() { return m_instanceData->props->y; } //-------------------------------------------------------------------------- //========================================================================== /// /// @ingroup cpp_kodi_addon_visualization_CB /// @brief Returns the width of the rendering window /// /// @return The width, in pixels /// inline int Width() { return m_instanceData->props->width; } //-------------------------------------------------------------------------- //========================================================================== /// /// @ingroup cpp_kodi_addon_visualization_CB /// @brief Returns the height of the rendering window /// /// @return The height, in pixels /// inline int Height() { return m_instanceData->props->height; } //-------------------------------------------------------------------------- //========================================================================== /// /// @ingroup cpp_kodi_addon_visualization_CB /// @brief Pixel aspect ratio (often abbreviated PAR) is a ratio that /// describes how the width of a pixel compares to the height of that pixel. /// /// @return The pixel aspect ratio used by the display /// inline float PixelRatio() { return m_instanceData->props->pixelRatio; } //-------------------------------------------------------------------------- //========================================================================== /// /// @ingroup cpp_kodi_addon_visualization_CB /// @brief Used to get the name of the add-on defined in `addon.xml` /// /// @return The add-on name /// inline std::string Name() { return m_instanceData->props->name; } //-------------------------------------------------------------------------- //========================================================================== /// /// @ingroup cpp_kodi_addon_visualization_CB /// @brief Used to get the full path where the add-on is installed /// /// @return The add-on installation path /// inline std::string Presets() { return m_instanceData->props->presets; } //-------------------------------------------------------------------------- //========================================================================== /// /// @ingroup cpp_kodi_addon_visualization_CB /// @brief Used to get the full path to the add-on's user profile /// /// @note The trailing folder (consisting of the add-on's ID) is not created /// by default. If it is needed, you must call kodi::vfs::CreateDirectory() /// to create the folder. /// /// @return Path to the user profile /// inline std::string Profile() { return m_instanceData->props->profile; } //-------------------------------------------------------------------------- //@} private: void SetAddonStruct(KODI_HANDLE instance) { if (instance == nullptr) throw std::logic_error("kodi::addon::CInstanceVisualization: Null pointer instance passed."); m_instanceData = static_cast(instance); m_instanceData->toAddon->addonInstance = this; m_instanceData->toAddon->start = ADDON_Start; m_instanceData->toAddon->stop = ADDON_Stop; m_instanceData->toAddon->audio_data = ADDON_AudioData; m_instanceData->toAddon->is_dirty = ADDON_IsDirty; m_instanceData->toAddon->render = ADDON_Render; m_instanceData->toAddon->get_info = ADDON_GetInfo; m_instanceData->toAddon->on_action = ADDON_OnAction; m_instanceData->toAddon->get_presets = ADDON_GetPresets; m_instanceData->toAddon->get_active_preset = ADDON_GetActivePreset; m_instanceData->toAddon->is_locked = ADDON_IsLocked; } inline static bool ADDON_Start(const AddonInstance_Visualization* addon, int channels, int samplesPerSec, int bitsPerSample, const char* songName) { addon->toAddon->addonInstance->m_renderHelper = kodi::gui::GetRenderHelper(); return addon->toAddon->addonInstance->Start(channels, samplesPerSec, bitsPerSample, songName); } inline static void ADDON_Stop(const AddonInstance_Visualization* addon) { addon->toAddon->addonInstance->Stop(); addon->toAddon->addonInstance->m_renderHelper = nullptr; } inline static void ADDON_AudioData(const AddonInstance_Visualization* addon, const float* audioData, int audioDataLength, float *freqData, int freqDataLength) { addon->toAddon->addonInstance->AudioData(audioData, audioDataLength, freqData, freqDataLength); } inline static bool ADDON_IsDirty(const AddonInstance_Visualization* addon) { return addon->toAddon->addonInstance->IsDirty(); } inline static void ADDON_Render(const AddonInstance_Visualization* addon) { if (!addon->toAddon->addonInstance->m_renderHelper) return; addon->toAddon->addonInstance->m_renderHelper->Begin(); addon->toAddon->addonInstance->Render(); addon->toAddon->addonInstance->m_renderHelper->End(); } inline static void ADDON_GetInfo(const AddonInstance_Visualization* addon, VIS_INFO *info) { addon->toAddon->addonInstance->GetInfo(info->bWantsFreq, info->iSyncDelay); } inline static bool ADDON_OnAction(const AddonInstance_Visualization* addon, VIS_ACTION action, const void *param) { switch (action) { case VIS_ACTION_NEXT_PRESET: return addon->toAddon->addonInstance->NextPreset(); case VIS_ACTION_PREV_PRESET: return addon->toAddon->addonInstance->PrevPreset(); case VIS_ACTION_LOAD_PRESET: return addon->toAddon->addonInstance->LoadPreset(*static_cast(param)); case VIS_ACTION_RANDOM_PRESET: return addon->toAddon->addonInstance->RandomPreset(); case VIS_ACTION_LOCK_PRESET: addon->toAddon->addonInstance->m_presetLockedByUser = !addon->toAddon->addonInstance->m_presetLockedByUser; return addon->toAddon->addonInstance->LockPreset(addon->toAddon->addonInstance->m_presetLockedByUser); case VIS_ACTION_RATE_PRESET_PLUS: return addon->toAddon->addonInstance->RatePreset(true); case VIS_ACTION_RATE_PRESET_MINUS: return addon->toAddon->addonInstance->RatePreset(false); case VIS_ACTION_UPDATE_ALBUMART: return addon->toAddon->addonInstance->UpdateAlbumart(static_cast(param)); case VIS_ACTION_UPDATE_TRACK: return addon->toAddon->addonInstance->UpdateTrack(*static_cast(param)); case VIS_ACTION_NONE: default: break; } return false; } inline static unsigned int ADDON_GetPresets(const AddonInstance_Visualization* addon) { std::vector presets; if (addon->toAddon->addonInstance->GetPresets(presets)) { for (auto it : presets) addon->toAddon->addonInstance->m_instanceData->toKodi->transfer_preset(addon->toKodi->kodiInstance, it.c_str()); } return static_cast(presets.size()); } inline static int ADDON_GetActivePreset(const AddonInstance_Visualization* addon) { return addon->toAddon->addonInstance->GetActivePreset(); } inline static bool ADDON_IsLocked(const AddonInstance_Visualization* addon) { return addon->toAddon->addonInstance->IsLocked(); } std::shared_ptr m_renderHelper; bool m_presetLockedByUser = false; AddonInstance_Visualization* m_instanceData; }; } /* namespace addon */ } /* namespace kodi */