#pragma once
/*
* Copyright (C) 2005-2017 Team Kodi
* http://kodi.tv
*
* This Program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2, or (at your option)
* any later version.
*
* This Program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Kodi; see the file COPYING. If not, see
* .
*
*/
/*
* Parts with a comment named "internal" are only used inside header and not
* used or accessed direct during add-on development!
*/
#include "../AddonBase.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);
} 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 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),
m_presetLockedByUser(false)
{
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(...)`.
///
/// @warning Only use `instance` from the CreateInstance call
///
CInstanceVisualization(KODI_HANDLE instance)
: IAddonInstance(ADDON_INSTANCE_VISUALIZATION),
m_presetLockedByUser(false)
{
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 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.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)
{
return addon->toAddon.addonInstance->Start(channels, samplesPerSec, bitsPerSample, songName);
}
inline static void ADDON_Stop(const AddonInstance_Visualization* addon)
{
addon->toAddon.addonInstance->Stop();
}
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)
{
addon->toAddon.addonInstance->Render();
}
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 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();
}
bool m_presetLockedByUser;
AddonInstance_Visualization* m_instanceData;
};
} /* namespace addon */
} /* namespace kodi */