aboutsummaryrefslogtreecommitdiffstats
path: root/src/jalv_internal.h
diff options
context:
space:
mode:
Diffstat (limited to 'src/jalv_internal.h')
-rw-r--r--src/jalv_internal.h612
1 files changed, 138 insertions, 474 deletions
diff --git a/src/jalv_internal.h b/src/jalv_internal.h
index 3bfc102..01d4c86 100644
--- a/src/jalv_internal.h
+++ b/src/jalv_internal.h
@@ -1,378 +1,130 @@
-/*
- Copyright 2007-2016 David Robillard <http://drobilla.net>
-
- Permission to use, copy, modify, and/or distribute this software for any
- purpose with or without fee is hereby granted, provided that the above
- copyright notice and this permission notice appear in all copies.
-
- THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
-*/
+// Copyright 2007-2022 David Robillard <d@drobilla.net>
+// SPDX-License-Identifier: ISC
#ifndef JALV_INTERNAL_H
#define JALV_INTERNAL_H
-#define _POSIX_C_SOURCE 200809L
-
-#include "lv2_evbuf.h"
+#include "attributes.h"
+#include "control.h"
+#include "jalv_config.h"
+#include "log.h"
+#include "nodes.h"
+#include "options.h"
#include "symap.h"
+#include "types.h"
+#include "urids.h"
+#include "worker.h"
#include "zix/ring.h"
#include "zix/sem.h"
-#include "zix/thread.h"
#include "lilv/lilv.h"
#include "serd/serd.h"
#include "sratom/sratom.h"
-#ifdef HAVE_SUIL
-#include "suil/suil.h"
+#if USE_SUIL
+# include "suil/suil.h"
#endif
-#include "lv2/atom/atom.h"
#include "lv2/atom/forge.h"
+#include "lv2/core/lv2.h"
#include "lv2/data-access/data-access.h"
#include "lv2/log/log.h"
-#include "lv2/midi/midi.h"
#include "lv2/options/options.h"
-#include "lv2/resize-port/resize-port.h"
#include "lv2/state/state.h"
#include "lv2/ui/ui.h"
#include "lv2/urid/urid.h"
#include "lv2/worker/worker.h"
+#include <stdbool.h>
+#include <stdint.h>
#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#ifdef HAVE_ISATTY
-# include <unistd.h>
-#endif
-
-#ifdef __clang__
-# define REALTIME __attribute__((annotate("realtime")))
-#else
-# define REALTIME
-#endif
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-typedef struct JalvBackend JalvBackend;
-
-typedef struct Jalv Jalv;
-
-enum PortFlow {
- FLOW_UNKNOWN,
- FLOW_INPUT,
- FLOW_OUTPUT
-};
-
-enum PortType {
- TYPE_UNKNOWN,
- TYPE_CONTROL,
- TYPE_AUDIO,
- TYPE_EVENT,
- TYPE_CV
-};
-
-struct Port {
- const LilvPort* lilv_port; ///< LV2 port
- enum PortType type; ///< Data type
- enum PortFlow flow; ///< Data flow direction
- void* sys_port; ///< For audio/MIDI ports, otherwise NULL
- LV2_Evbuf* evbuf; ///< For MIDI ports, otherwise NULL
- void* widget; ///< Control widget, if applicable
- size_t buf_size; ///< Custom buffer size, or 0
- uint32_t index; ///< Port index
- float control; ///< For control ports, otherwise 0.0f
-};
-
-/* Controls */
-
-/** Type of plugin control. */
-typedef enum {
- PORT, ///< Control port
- PROPERTY ///< Property (set via atom message)
-} ControlType;
-
-typedef struct {
- float value;
- char* label;
-} ScalePoint;
-
-/** Order scale points by value. */
-int scale_point_cmp(const ScalePoint* a, const ScalePoint* b);
-
-/** Plugin control. */
-typedef struct {
- Jalv* jalv;
- ControlType type;
- LilvNode* node;
- LilvNode* symbol; ///< Symbol
- LilvNode* label; ///< Human readable label
- LV2_URID property; ///< Iff type == PROPERTY
- uint32_t index; ///< Iff type == PORT
- LilvNode* group; ///< Port/control group, or NULL
- void* widget; ///< Control Widget
- size_t n_points; ///< Number of scale points
- ScalePoint* points; ///< Scale points
- LV2_URID value_type; ///< Type of control value
- LilvNode* min; ///< Minimum value
- LilvNode* max; ///< Maximum value
- LilvNode* def; ///< Default value
- bool is_toggle; ///< Boolean (0 and 1 only)
- bool is_integer; ///< Integer values only
- bool is_enumeration; ///< Point values only
- bool is_logarithmic; ///< Logarithmic scale
- bool is_writable; ///< Writable (input)
- bool is_readable; ///< Readable (output)
-} ControlID;
-ControlID*
-new_port_control(Jalv* jalv, uint32_t index);
-
-ControlID*
-new_property_control(Jalv* jalv, const LilvNode* property);
-
-typedef struct {
- size_t n_controls;
- ControlID** controls;
-} Controls;
-
-void
-add_control(Controls* controls, ControlID* control);
-
-ControlID*
-get_property_control(const Controls* controls, LV2_URID property);
-
-/**
- Control change event, sent through ring buffers for UI updates.
-*/
-typedef struct {
- uint32_t index;
- uint32_t protocol;
- uint32_t size;
- uint8_t body[];
-} ControlChange;
-
-typedef struct {
- char* name; ///< Client name
- int name_exact; ///< Exit if name is taken
- char* uuid; ///< Session UUID
- char* load; ///< Path for state to load
- char* preset; ///< URI of preset to load
- char** controls; ///< Control values
- uint32_t buffer_size; ///< Plugin <= >UI communication buffer size
- double update_rate; ///< UI update rate in Hz
- int dump; ///< Dump communication iff true
- int trace; ///< Print trace log iff true
- int generic_ui; ///< Use generic UI iff true
- int show_hidden; ///< Show controls for notOnGUI ports
- int no_menu; ///< Hide menu iff true
- int show_ui; ///< Show non-embedded UI
- int print_controls; ///< Print control changes to stdout
- int non_interactive; ///< Do not listen for commands on stdin
- char* ui_uri; ///< URI of UI to load
-} JalvOptions;
-
-typedef struct {
- LV2_URID atom_Float;
- LV2_URID atom_Int;
- LV2_URID atom_Object;
- LV2_URID atom_Path;
- LV2_URID atom_String;
- LV2_URID atom_eventTransfer;
- LV2_URID bufsz_maxBlockLength;
- LV2_URID bufsz_minBlockLength;
- LV2_URID bufsz_sequenceSize;
- LV2_URID log_Error;
- LV2_URID log_Trace;
- LV2_URID log_Warning;
- LV2_URID midi_MidiEvent;
- LV2_URID param_sampleRate;
- LV2_URID patch_Get;
- LV2_URID patch_Put;
- LV2_URID patch_Set;
- LV2_URID patch_body;
- LV2_URID patch_property;
- LV2_URID patch_value;
- LV2_URID time_Position;
- LV2_URID time_bar;
- LV2_URID time_barBeat;
- LV2_URID time_beatUnit;
- LV2_URID time_beatsPerBar;
- LV2_URID time_beatsPerMinute;
- LV2_URID time_frame;
- LV2_URID time_speed;
- LV2_URID ui_updateRate;
-} JalvURIDs;
+JALV_BEGIN_DECLS
typedef struct {
- LilvNode* atom_AtomPort;
- LilvNode* atom_Chunk;
- LilvNode* atom_Float;
- LilvNode* atom_Path;
- LilvNode* atom_Sequence;
- LilvNode* lv2_AudioPort;
- LilvNode* lv2_CVPort;
- LilvNode* lv2_ControlPort;
- LilvNode* lv2_InputPort;
- LilvNode* lv2_OutputPort;
- LilvNode* lv2_connectionOptional;
- LilvNode* lv2_control;
- LilvNode* lv2_default;
- LilvNode* lv2_enumeration;
- LilvNode* lv2_extensionData;
- LilvNode* lv2_integer;
- LilvNode* lv2_maximum;
- LilvNode* lv2_minimum;
- LilvNode* lv2_name;
- LilvNode* lv2_reportsLatency;
- LilvNode* lv2_sampleRate;
- LilvNode* lv2_symbol;
- LilvNode* lv2_toggled;
- LilvNode* midi_MidiEvent;
- LilvNode* pg_group;
- LilvNode* pprops_logarithmic;
- LilvNode* pprops_notOnGUI;
- LilvNode* pprops_rangeSteps;
- LilvNode* pset_Preset;
- LilvNode* pset_bank;
- LilvNode* rdfs_comment;
- LilvNode* rdfs_label;
- LilvNode* rdfs_range;
- LilvNode* rsz_minimumSize;
- LilvNode* ui_showInterface;
- LilvNode* work_interface;
- LilvNode* work_schedule;
- LilvNode* end; ///< NULL terminator for easy freeing of entire structure
-} JalvNodes;
-
-typedef enum {
- JALV_RUNNING,
- JALV_PAUSE_REQUESTED,
- JALV_PAUSED
-} JalvPlayState;
-
-typedef struct {
- Jalv* jalv; ///< Pointer back to Jalv
- ZixRing* requests; ///< Requests to the worker
- ZixRing* responses; ///< Responses from the worker
- void* response; ///< Worker response buffer
- ZixSem sem; ///< Worker semaphore
- ZixThread thread; ///< Worker thread
- const LV2_Worker_Interface* iface; ///< Plugin worker interface
- bool threaded; ///< Run work in another thread
-} JalvWorker;
-
-typedef struct {
- LV2_Feature map_feature;
- LV2_Feature unmap_feature;
- LV2_State_Make_Path make_path;
- LV2_Feature make_path_feature;
- LV2_Worker_Schedule sched;
- LV2_Feature sched_feature;
- LV2_Worker_Schedule ssched;
- LV2_Feature state_sched_feature;
- LV2_Log_Log llog;
- LV2_Feature log_feature;
- LV2_Options_Option options[6];
- LV2_Feature options_feature;
- LV2_Feature safe_restore_feature;
- LV2UI_Request_Value request_value;
- LV2_Feature request_value_feature;
- LV2_Extension_Data_Feature ext_data;
+ LV2_Feature map_feature;
+ LV2_Feature unmap_feature;
+ LV2_State_Make_Path make_path;
+ LV2_Feature make_path_feature;
+ LV2_Worker_Schedule sched;
+ LV2_Feature sched_feature;
+ LV2_Worker_Schedule ssched;
+ LV2_Feature state_sched_feature;
+ LV2_Log_Log llog;
+ LV2_Feature log_feature;
+ LV2_Options_Option options[7];
+ LV2_Feature options_feature;
+ LV2_Feature safe_restore_feature;
+ LV2UI_Request_Value request_value;
+ LV2_Feature request_value_feature;
+ LV2_Extension_Data_Feature ext_data;
} JalvFeatures;
-struct Jalv {
- JalvOptions opts; ///< Command-line options
- JalvURIDs urids; ///< URIDs
- JalvNodes nodes; ///< Nodes
- LV2_Atom_Forge forge; ///< Atom forge
- const char* prog_name; ///< Program name (argv[0])
- LilvWorld* world; ///< Lilv World
- LV2_URID_Map map; ///< URI => Int map
- LV2_URID_Unmap unmap; ///< Int => URI map
- SerdEnv* env; ///< Environment for RDF printing
- Sratom* sratom; ///< Atom serialiser
- Sratom* ui_sratom; ///< Atom serialiser for UI thread
- Symap* symap; ///< URI map
- ZixSem symap_lock; ///< Lock for URI map
- JalvBackend* backend; ///< Audio system backend
- ZixRing* ui_events; ///< Port events from UI
- ZixRing* plugin_events; ///< Port events from plugin
- void* ui_event_buf; ///< Buffer for reading UI port events
- JalvWorker worker; ///< Worker thread implementation
- JalvWorker state_worker; ///< Synchronous worker for state restore
- ZixSem work_lock; ///< Lock for plugin work() method
- ZixSem done; ///< Exit semaphore
- ZixSem paused; ///< Paused signal from process thread
- JalvPlayState play_state; ///< Current play state
- char* temp_dir; ///< Temporary plugin state directory
- char* save_dir; ///< Plugin save directory
- const LilvPlugin* plugin; ///< Plugin class (RDF data)
- LilvState* preset; ///< Current preset
- LilvUIs* uis; ///< All plugin UIs (RDF data)
- const LilvUI* ui; ///< Plugin UI (RDF data)
- const LilvNode* ui_type; ///< Plugin UI type (unwrapped)
- LilvInstance* instance; ///< Plugin instance (shared library)
-#ifdef HAVE_SUIL
- SuilHost* ui_host; ///< Plugin UI host support
- SuilInstance* ui_instance; ///< Plugin UI instance (shared library)
+struct JalvImpl {
+ JalvOptions opts; ///< Command-line options
+ JalvURIDs urids; ///< URIDs
+ JalvNodes nodes; ///< Nodes
+ JalvLog log; ///< Log for error/warning/debug messages
+ LV2_Atom_Forge forge; ///< Atom forge
+ LilvWorld* world; ///< Lilv World
+ LV2_URID_Map map; ///< URI => Int map
+ LV2_URID_Unmap unmap; ///< Int => URI map
+ SerdEnv* env; ///< Environment for RDF printing
+ Sratom* sratom; ///< Atom serialiser
+ Sratom* ui_sratom; ///< Atom serialiser for UI thread
+ Symap* symap; ///< URI map
+ ZixSem symap_lock; ///< Lock for URI map
+ JalvBackend* backend; ///< Audio system backend
+ ZixRing* ui_to_plugin; ///< Port events from UI
+ ZixRing* plugin_to_ui; ///< Port events from plugin
+ void* ui_event_buf; ///< Buffer for reading UI port events
+ JalvWorker* worker; ///< Worker thread implementation
+ JalvWorker* state_worker; ///< Synchronous worker for state restore
+ ZixSem work_lock; ///< Lock for plugin work() method
+ ZixSem done; ///< Exit semaphore
+ ZixSem paused; ///< Paused signal from process thread
+ JalvPlayState play_state; ///< Current play state
+ char* temp_dir; ///< Temporary plugin state directory
+ char* save_dir; ///< Plugin save directory
+ const LilvPlugin* plugin; ///< Plugin class (RDF data)
+ LilvState* preset; ///< Current preset
+ LilvUIs* uis; ///< All plugin UIs (RDF data)
+ const LilvUI* ui; ///< Plugin UI (RDF data)
+ const LilvNode* ui_type; ///< Plugin UI type (unwrapped)
+ LilvInstance* instance; ///< Plugin instance (shared library)
+#if USE_SUIL
+ SuilHost* ui_host; ///< Plugin UI host support
+ SuilInstance* ui_instance; ///< Plugin UI instance (shared library)
#endif
- void* window; ///< Window (if applicable)
- struct Port* ports; ///< Port array of size num_ports
- Controls controls; ///< Available plugin controls
- uint32_t block_length; ///< Audio buffer size (block length)
- size_t midi_buf_size; ///< Size of MIDI port buffers
- uint32_t control_in; ///< Index of control input port
- uint32_t num_ports; ///< Size of the two following arrays:
- uint32_t plugin_latency; ///< Latency reported by plugin (if any)
- float ui_update_hz; ///< Frequency of UI updates
- float sample_rate; ///< Sample rate
- uint32_t event_delta_t; ///< Frames since last update sent to UI
- uint32_t position; ///< Transport position in frames
- float bpm; ///< Transport tempo in beats per minute
- bool rolling; ///< Transport speed (0=stop, 1=play)
- bool buf_size_set; ///< True iff buffer size callback fired
- bool exit; ///< True iff execution is finished
- bool has_ui; ///< True iff a control UI is present
- bool request_update; ///< True iff a plugin update is needed
- bool safe_restore; ///< Plugin restore() is thread-safe
- JalvFeatures features;
- const LV2_Feature** feature_list;
+ void* window; ///< Window (if applicable)
+ struct Port* ports; ///< Port array of size num_ports
+ Controls controls; ///< Available plugin controls
+ uint32_t block_length; ///< Audio buffer size (block length)
+ size_t midi_buf_size; ///< Size of MIDI port buffers
+ uint32_t control_in; ///< Index of control input port
+ uint32_t num_ports; ///< Size of the two following arrays:
+ uint32_t plugin_latency; ///< Latency reported by plugin (if any)
+ float ui_update_hz; ///< Frequency of UI updates
+ float ui_scale_factor; ///< UI scale factor
+ float sample_rate; ///< Sample rate
+ uint32_t event_delta_t; ///< Frames since last update sent to UI
+ uint32_t position; ///< Transport position in frames
+ float bpm; ///< Transport tempo in beats per minute
+ bool rolling; ///< Transport speed (0=stop, 1=play)
+ bool buf_size_set; ///< True iff buffer size callback fired
+ bool has_ui; ///< True iff a control UI is present
+ bool request_update; ///< True iff a plugin update is needed
+ bool safe_restore; ///< Plugin restore() is thread-safe
+ JalvFeatures features;
+ const LV2_Feature** feature_list;
};
int
jalv_open(Jalv* jalv, int* argc, char*** argv);
int
-jalv_init(int* argc, char*** argv, JalvOptions* opts);
-
-int
jalv_close(Jalv* jalv);
-JalvBackend*
-jalv_backend_init(Jalv* jalv);
-
-void
-jalv_backend_activate(Jalv* jalv);
-
-void
-jalv_backend_deactivate(Jalv* jalv);
-
-void
-jalv_backend_close(Jalv* jalv);
-
-/** Expose a port to the system (if applicable) and connect it to its buffer. */
-void
-jalv_backend_activate_port(Jalv* jalv, uint32_t port_index);
-
void
jalv_create_ports(Jalv* jalv);
@@ -389,50 +141,31 @@ ControlID*
jalv_control_by_symbol(Jalv* jalv, const char* sym);
void
-jalv_set_control(const ControlID* control,
+jalv_set_control(Jalv* jalv,
+ const ControlID* control,
uint32_t size,
LV2_URID type,
const void* body);
-const char*
-jalv_native_ui_type(void);
-
-bool
-jalv_discover_ui(Jalv* jalv);
-
-float
-jalv_ui_refresh_rate(Jalv* jalv);
-
-int
-jalv_open_ui(Jalv* jalv);
-
void
jalv_init_ui(Jalv* jalv);
-int
-jalv_close_ui(Jalv* jalv);
-
void
-jalv_ui_instantiate(Jalv* jalv,
- const char* native_ui_type,
- void* parent);
+jalv_ui_instantiate(Jalv* jalv, const char* native_ui_type, void* parent);
bool
jalv_ui_is_resizable(Jalv* jalv);
void
-jalv_ui_write(void* const controller,
- uint32_t port_index,
- uint32_t buffer_size,
- uint32_t protocol,
- const void* buffer);
+jalv_send_to_plugin(void* jalv_handle,
+ uint32_t port_index,
+ uint32_t buffer_size,
+ uint32_t protocol,
+ const void* buffer);
void
jalv_apply_ui_events(Jalv* jalv, uint32_t nframes);
-uint32_t
-jalv_ui_port_index(void* const controller, const char* symbol);
-
void
jalv_ui_port_event(Jalv* jalv,
uint32_t port_index,
@@ -440,127 +173,58 @@ jalv_ui_port_event(Jalv* jalv,
uint32_t protocol,
const void* buffer);
-bool
-jalv_send_to_ui(Jalv* jalv,
- uint32_t port_index,
- uint32_t type,
- uint32_t size,
- const void* body);
-bool
-jalv_run(Jalv* jalv, uint32_t nframes);
-
-int
-jalv_update(Jalv* jalv);
-
-typedef int (*PresetSink)(Jalv* jalv,
- const LilvNode* node,
- const LilvNode* title,
- void* data);
+/**
+ Write a port event using the atom:eventTransfer protocol.
-int
-jalv_load_presets(Jalv* jalv, PresetSink sink, void* data);
+ This is used to transfer atoms between the plugin and UI via sequence ports.
+ @param jalv Jalv instance.
+ @param target Communication ring (jalv->plugin_to_ui or jalv->ui_to_plugin).
+ @param port_index Index of the port this change is for.
+ @param size Size of body in bytes.
+ @param type Atom type URID.
+ @param body Atom body.
+ @return 0 on success, non-zero on failure (overflow).
+*/
int
-jalv_unload_presets(Jalv* jalv);
+jalv_write_event(Jalv* jalv,
+ ZixRing* target,
+ uint32_t port_index,
+ uint32_t size,
+ LV2_URID type,
+ const void* body);
-int
-jalv_apply_preset(Jalv* jalv, const LilvNode* preset);
+/**
+ Write a control port change using the default (0) protocol.
-int
-jalv_delete_current_preset(Jalv* jalv);
+ This is used to transfer control port value changes between the plugin and
+ UI.
+ @param jalv Jalv instance.
+ @param target Communication ring (jalv->plugin_to_ui or jalv->ui_to_plugin).
+ @param port_index Index of the port this change is for.
+ @param value New control port value.
+ @return 0 on success, non-zero on failure (overflow).
+*/
int
-jalv_save_preset(Jalv* jalv,
- const char* dir,
- const char* uri,
- const char* label,
- const char* filename);
+jalv_write_control(Jalv* jalv,
+ ZixRing* target,
+ uint32_t port_index,
+ float value);
void
-jalv_save(Jalv* jalv, const char* dir);
+jalv_dump_atom(Jalv* jalv,
+ FILE* stream,
+ const char* label,
+ const LV2_Atom* atom,
+ int color);
-void
-jalv_save_port_values(Jalv* jalv,
- SerdWriter* writer,
- const SerdNode* subject);
-char*
-jalv_make_path(LV2_State_Make_Path_Handle handle,
- const char* path);
-
-void
-jalv_apply_state(Jalv* jalv, LilvState* state);
-
-char*
-atom_to_turtle(LV2_URID_Unmap* unmap,
- const SerdNode* subject,
- const SerdNode* predicate,
- const LV2_Atom* atom);
-
-static inline void
-jalv_print_control(Jalv* jalv, const struct Port* port, float value)
-{
- const LilvNode* sym = lilv_port_get_symbol(jalv->plugin, port->lilv_port);
- printf("%s = %f\n", lilv_node_as_string(sym), value);
-}
-
-static inline char*
-jalv_strdup(const char* str)
-{
- const size_t len = strlen(str);
- char* copy = (char*)malloc(len + 1);
- memcpy(copy, str, len + 1);
- return copy;
-}
-
-static inline char*
-jalv_strjoin(const char* a, const char* b)
-{
- const size_t a_len = strlen(a);
- const size_t b_len = strlen(b);
- char* const out = (char*)malloc(a_len + b_len + 1);
-
- memcpy(out, a, a_len);
- memcpy(out + a_len, b, b_len);
- out[a_len + b_len] = '\0';
-
- return out;
-}
-
-int
-jalv_printf(LV2_Log_Handle handle,
- LV2_URID type,
- const char* fmt, ...);
+bool
+jalv_run(Jalv* jalv, uint32_t nframes);
int
-jalv_vprintf(LV2_Log_Handle handle,
- LV2_URID type,
- const char* fmt,
- va_list ap);
-
-static inline bool
-jalv_ansi_start(FILE* stream, int color)
-{
-#if defined(HAVE_ISATTY) && defined(HAVE_FILENO)
- if (isatty(fileno(stream))) {
- return fprintf(stream, "\033[0;%dm", color);
- }
-#endif
- return 0;
-}
-
-static inline void
-jalv_ansi_reset(FILE* stream)
-{
-#ifdef HAVE_ISATTY
- if (isatty(fileno(stream))) {
- fprintf(stream, "\033[0m");
- fflush(stream);
- }
-#endif
-}
+jalv_update(Jalv* jalv);
-#ifdef __cplusplus
-} // extern "C"
-#endif
+JALV_END_DECLS
-#endif // JALV_INTERNAL_H
+#endif // JALV_INTERNAL_H