remove/hide Session::tempo_map() and use TempoMap::use() instead (thread local shared ptr)
This commit is contained in:
parent
60d1c5f722
commit
79763893b3
|
@ -828,8 +828,8 @@ public:
|
|||
bool loop_is_possible () const;
|
||||
PBD::Signal0<void> PunchLoopConstraintChange;
|
||||
|
||||
Temporal::TempoMap& tempo_map() { *Temporal::TempoMap::fetch(); }
|
||||
const Temporal::TempoMap& tempo_map() const { return *Temporal::TempoMap::fetch(); }
|
||||
// Temporal::TempoMap& tempo_map() { *Temporal::TempoMap::fetch(); }
|
||||
// const Temporal::TempoMap& tempo_map() const { return *Temporal::TempoMap::fetch(); }
|
||||
void maybe_update_tempo_from_midiclock_tempo (float bpm);
|
||||
|
||||
unsigned int get_xrun_count () const {return _xrun_count; }
|
||||
|
|
|
@ -2509,7 +2509,8 @@ LuaBindings::common (lua_State* L)
|
|||
.addFunction ("master_out", &Session::master_out)
|
||||
.addFunction ("add_internal_send", (void (Session::*)(boost::shared_ptr<Route>, boost::shared_ptr<Processor>, boost::shared_ptr<Route>))&Session::add_internal_send)
|
||||
.addFunction ("add_internal_sends", &Session::add_internal_sends)
|
||||
.addFunction ("tempo_map", (TempoMap& (Session::*)())&Session::tempo_map)
|
||||
#warning NUTEMPO allow lua access to the tempo map
|
||||
// .addFunction ("tempo_map", (TempoMap& (Session::*)())&Session::tempo_map)
|
||||
.addFunction ("locations", &Session::locations)
|
||||
.addFunction ("soloing", &Session::soloing)
|
||||
.addFunction ("listening", &Session::listening)
|
||||
|
|
|
@ -2645,9 +2645,9 @@ LV2Plugin::connect_and_run(BufferSet& bufs,
|
|||
speed = end > 0 ? speed : 0;
|
||||
samplepos_t start0 = std::max (samplepos_t (0), start);
|
||||
|
||||
TempoMap& tmap = _session.tempo_map();
|
||||
TempoMap::SharedPtr tmap (TempoMap::use());
|
||||
TempoMapPoints tempo_map_points;
|
||||
tmap.get_grid (tempo_map_points, start, end, 0);
|
||||
tmap->get_grid (tempo_map_points, start, end, 0);
|
||||
TempoMapPoints::const_iterator tempo_map_point = tempo_map_points.begin();
|
||||
TempoMapPoint first_tempo_map_point = tempo_map_points.front();
|
||||
|
||||
|
@ -2659,7 +2659,7 @@ LV2Plugin::connect_and_run(BufferSet& bufs,
|
|||
|
||||
float bpm = tmap.tempo_at_sample (start0).note_types_per_minute();
|
||||
TempoMapPoints tempo_map_points;
|
||||
tmap.get_grid (tempo_map_points, start0, end, 0);
|
||||
tmap->get_grid (tempo_map_points, start0, end, 0);
|
||||
TempoMapPoint first_tempo_map_point = tempo_map_points.front();
|
||||
|
||||
/* note that this is not necessarily quarter notes */
|
||||
|
@ -3123,7 +3123,7 @@ LV2Plugin::connect_and_run(BufferSet& bufs,
|
|||
* Note: for no-midi plugins, we only ever send information at cycle-start,
|
||||
* so it needs to be realative to that.
|
||||
*/
|
||||
TempoMetric metric (tmap.metric_at (start));
|
||||
TempoMetric metric (tmap->metric_at (start));
|
||||
_current_bpm = metric.tempo().note_types_per_minute();
|
||||
Temporal::BBT_Time bbt (metric.bbt_at (start));
|
||||
double beatpos = (bbt.bars - 1) * metric.divisions_per_bar()
|
||||
|
|
|
@ -157,7 +157,7 @@ MIDIClock_TransportMaster::pre_process (MIDI::pframes_t nframes, samplepos_t now
|
|||
void
|
||||
MIDIClock_TransportMaster::calculate_one_ppqn_in_samples_at(samplepos_t time)
|
||||
{
|
||||
const Temporal::TempoMetric& metric = _session->tempo_map().metric_at (time);
|
||||
const Temporal::TempoMetric& metric = Temporal::TempoMap::use()->metric_at (time);
|
||||
const double samples_per_quarter_note = metric.tempo().samples_per_quarter_note (ENGINE->sample_rate());
|
||||
|
||||
one_ppqn_in_samples = samples_per_quarter_note / double (ppqn);
|
||||
|
|
|
@ -272,7 +272,8 @@ Session::Session (AudioEngine &eng,
|
|||
, ltc_timecode_offset (0)
|
||||
, ltc_timecode_negative_offset (false)
|
||||
, midi_control_ui (0)
|
||||
, _tempo_map (0)
|
||||
, _punch_or_loop (NoConstraint)
|
||||
, current_usecs_per_track (1000)
|
||||
, _all_route_group (new RouteGroup (*this, "all"))
|
||||
, routes (new RouteList)
|
||||
, _adding_routes_in_progress (false)
|
||||
|
@ -429,7 +430,6 @@ Session::Session (AudioEngine &eng,
|
|||
midi_control_ui = 0;
|
||||
_punch_or_loop = NoConstraint;
|
||||
current_usecs_per_track = 1000;
|
||||
_tempo_map = 0;
|
||||
_all_route_group = new RouteGroup (*this, "all");
|
||||
_adding_routes_in_progress = false;
|
||||
_reconnecting_routes_in_progress = false;
|
||||
|
@ -915,7 +915,6 @@ Session::destroy ()
|
|||
delete _locations; _locations = 0;
|
||||
|
||||
delete midi_clock;
|
||||
delete _tempo_map;
|
||||
|
||||
/* clear event queue, the session is gone, nobody is interested in
|
||||
* those anymore, but they do leak memory if not removed
|
||||
|
@ -2195,7 +2194,7 @@ Session::preroll_samples (samplepos_t pos) const
|
|||
{
|
||||
const float pr = Config->get_preroll_seconds();
|
||||
if (pos >= 0 && pr < 0) {
|
||||
Temporal::TempoMetric const & metric (_tempo_map->metric_at (pos));
|
||||
Temporal::TempoMetric const & metric (TempoMap::use()->metric_at (pos));
|
||||
return metric.samples_per_bar (sample_rate()) * -pr;
|
||||
}
|
||||
if (pr < 0) {
|
||||
|
@ -7427,10 +7426,12 @@ Session::listening () const
|
|||
void
|
||||
Session::maybe_update_tempo_from_midiclock_tempo (float bpm)
|
||||
{
|
||||
if (_tempo_map->n_tempos() == 1) {
|
||||
Temporal::TempoMetric const & metric (_tempo_map->metric_at (0));
|
||||
TempoMap::SharedPtr tmap (TempoMap::use());
|
||||
|
||||
if (tmap->n_tempos() == 1) {
|
||||
Temporal::TempoMetric const & metric (tmap->metric_at (0));
|
||||
if (fabs (metric.tempo().note_types_per_minute() - bpm) > (0.01 * metric.tempo().note_types_per_minute())) {
|
||||
_tempo_map->change_tempo (metric.tempo(), Tempo (bpm, 4.0, bpm));
|
||||
tmap->change_tempo (metric.tempo(), Tempo (bpm, 4.0, bpm));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -124,7 +124,7 @@ Session::click (samplepos_t cycle_start, samplecnt_t nframes)
|
|||
const samplepos_t end = start + move;
|
||||
|
||||
_click_points.clear ();
|
||||
_tempo_map->get_grid (_click_points, samples_to_superclock (start, sample_rate()), samples_to_superclock (end, sample_rate()));
|
||||
TempoMap::use()->get_grid (_click_points, samples_to_superclock (start, sample_rate()), samples_to_superclock (end, sample_rate()));
|
||||
|
||||
if (distance (_click_points.begin(), _click_points.end()) == 0) {
|
||||
start += move;
|
||||
|
|
|
@ -110,8 +110,8 @@ Session::memento_command_factory(XMLNode *n)
|
|||
} else if (type_name == "ARDOUR::Locations") {
|
||||
return new MementoCommand<Locations>(*_locations, before, after);
|
||||
|
||||
} else if (type_name == "ARDOUR::TempoMap") {
|
||||
return new MementoCommand<TempoMap>(*_tempo_map, before, after);
|
||||
} else if (type_name == "Temporal::TempoMap") {
|
||||
return new MementoCommand<TempoMap>(*TempoMap::use(), before, after);
|
||||
|
||||
} else if (type_name == "ARDOUR::Playlist" || type_name == "ARDOUR::AudioPlaylist" || type_name == "ARDOUR::MidiPlaylist") {
|
||||
if (boost::shared_ptr<Playlist> pl = _playlists->by_name(child->property("name")->value())) {
|
||||
|
|
|
@ -260,19 +260,7 @@ Session::post_engine_init ()
|
|||
_engine.Halted.connect_same_thread (*this, boost::bind (&Session::engine_halted, this));
|
||||
_engine.Xrun.connect_same_thread (*this, boost::bind (&Session::xrun_recovery, this));
|
||||
|
||||
try {
|
||||
/* tempo map requires sample rate knowledge */
|
||||
|
||||
delete _tempo_map;
|
||||
_tempo_map = new Temporal::TempoMap (Temporal::Tempo (120), Temporal::Meter (4, 4));
|
||||
_tempo_map->Changed.connect_same_thread (*this, boost::bind (&Session::tempo_map_changed, this));
|
||||
} catch (std::exception const & e) {
|
||||
error << _("Unexpected exception during session setup: ") << e.what() << endmsg;
|
||||
return -2;
|
||||
} catch (...) {
|
||||
error << _("Unknown exception during session setup") << endmsg;
|
||||
return -3;
|
||||
}
|
||||
#warning NUTEMPO does session need to know about tempo map changes?
|
||||
|
||||
try {
|
||||
/* MidiClock requires a tempo map */
|
||||
|
|
|
@ -41,6 +41,7 @@
|
|||
using namespace std;
|
||||
using namespace ARDOUR;
|
||||
using namespace PBD;
|
||||
using namespace Temporal;
|
||||
|
||||
#define TFSM_EVENT(evtype) { _transport_fsm->enqueue (new TransportFSM::Event (evtype)); }
|
||||
|
||||
|
@ -49,7 +50,7 @@ using namespace PBD;
|
|||
void
|
||||
Session::bbt_time (timepos_t const & when, Temporal::BBT_Time& bbt)
|
||||
{
|
||||
bbt = _tempo_map->bbt_at (when);
|
||||
bbt = TempoMap::use()->bbt_at (when);
|
||||
}
|
||||
|
||||
/* Timecode TIME */
|
||||
|
@ -241,7 +242,7 @@ Session::convert_to_samples (AnyTime const & position)
|
|||
|
||||
switch (position.type) {
|
||||
case AnyTime::BBT:
|
||||
return Temporal::superclock_to_samples (_tempo_map->superclock_at (position.bbt), _current_sample_rate);
|
||||
return Temporal::superclock_to_samples (TempoMap::use()->superclock_at (position.bbt), _current_sample_rate);
|
||||
break;
|
||||
|
||||
case AnyTime::Timecode:
|
||||
|
@ -277,7 +278,7 @@ Session::any_duration_to_samples (samplepos_t position, AnyTime const & duration
|
|||
|
||||
switch (duration.type) {
|
||||
case AnyTime::BBT:
|
||||
return Temporal::superclock_to_samples (_tempo_map->superclock_plus_bbt (Temporal::samples_to_superclock (position, _current_sample_rate), duration.bbt), _current_sample_rate) - position;
|
||||
return Temporal::superclock_to_samples (TempoMap::use()->superclock_plus_bbt (Temporal::samples_to_superclock (position, _current_sample_rate), duration.bbt), _current_sample_rate) - position;
|
||||
break;
|
||||
|
||||
case AnyTime::Timecode:
|
||||
|
|
|
@ -522,16 +522,18 @@ Session::start_transport (bool after_loop)
|
|||
|
||||
if ((actively_recording () || (config.get_punch_in () && get_record_enabled ()))
|
||||
&& click_data && (config.get_count_in () || _count_in_once)) {
|
||||
TempoMap::SharedPtr tmap (TempoMap::use());
|
||||
|
||||
_count_in_once = false;
|
||||
/* calculate count-in duration (in audio samples)
|
||||
* - use [fixed] tempo/meter at _transport_sample
|
||||
* - calc duration of 1 bar + time-to-beat before or at transport_sample
|
||||
*/
|
||||
TempoMetric const & tempometric = _tempo_map->metric_at (_transport_sample);
|
||||
TempoMetric const & tempometric = tmap->metric_at (_transport_sample);
|
||||
|
||||
const double num = tempometric.divisions_per_bar ();
|
||||
/* XXX possible optimization: get meter and BBT time in one call */
|
||||
const Temporal::BBT_Time bbt = _tempo_map->bbt_at (_transport_sample);
|
||||
const Temporal::BBT_Time bbt = tmap->bbt_at (_transport_sample);
|
||||
const double bar_fract = (double) bbt.beats / tempometric.divisions_per_bar();
|
||||
|
||||
_count_in_samples = tempometric.samples_per_bar (_current_sample_rate);
|
||||
|
|
|
@ -75,6 +75,7 @@ intptr_t Session::vst_callback (
|
|||
static VstTimeInfo _timeinfo; // only uses as fallback
|
||||
VstTimeInfo* timeinfo;
|
||||
int32_t newflags = 0;
|
||||
TempoMap::SharedPtr tmap (TempoMap::use());
|
||||
|
||||
if (effect && effect->ptr1) {
|
||||
plug = (VSTPlugin *) (effect->ptr1);
|
||||
|
@ -188,12 +189,12 @@ intptr_t Session::vst_callback (
|
|||
timeinfo->sampleRate = session->sample_rate();
|
||||
|
||||
if (value & (kVstTempoValid)) {
|
||||
const Tempo& t (session->tempo_map().metric_at (now).tempo());
|
||||
const Tempo& t (tmap->metric_at (now).tempo());
|
||||
timeinfo->tempo = t.quarter_notes_per_minute ();
|
||||
newflags |= (kVstTempoValid);
|
||||
}
|
||||
if (value & (kVstTimeSigValid)) {
|
||||
const Meter& ms (session->tempo_map().metric_at (now).meter());
|
||||
const Meter& ms (tmap->metric_at (now).meter());
|
||||
timeinfo->timeSigNumerator = ms.divisions_per_bar ();
|
||||
timeinfo->timeSigDenominator = ms.note_value ();
|
||||
newflags |= (kVstTimeSigValid);
|
||||
|
@ -202,13 +203,13 @@ intptr_t Session::vst_callback (
|
|||
Temporal::BBT_Time bbt;
|
||||
|
||||
try {
|
||||
bbt = session->tempo_map().bbt_at (now);
|
||||
bbt = tmap->bbt_at (now);
|
||||
bbt.beats = 1;
|
||||
bbt.ticks = 0;
|
||||
/* exact quarter note */
|
||||
double ppqBar = session->tempo_map().quarter_note_at (bbt);
|
||||
double ppqBar = tmap->quarter_note_at (bbt);
|
||||
/* quarter note at sample position (not rounded to note subdivision) */
|
||||
double ppqPos = session->tempo_map().quarter_note_at (now);
|
||||
double ppqPos = tmap->quarter_note_at (now);
|
||||
if (value & (kVstPpqPosValid)) {
|
||||
timeinfo->ppqPos = ppqPos;
|
||||
newflags |= kVstPpqPosValid;
|
||||
|
@ -314,7 +315,7 @@ intptr_t Session::vst_callback (
|
|||
SHOW_CALLBACK ("audioMasterTempoAt");
|
||||
// returns tempo (in bpm * 10000) at sample sample location passed in <value>
|
||||
if (session) {
|
||||
const Tempo& t (session->tempo_map().metric_at (value).tempo());
|
||||
const Tempo& t (tmap->metric_at (value).tempo());
|
||||
return t.quarter_notes_per_minute() * 1000;
|
||||
} else {
|
||||
return 0;
|
||||
|
|
|
@ -31,6 +31,7 @@
|
|||
using namespace std;
|
||||
using namespace PBD;
|
||||
using namespace ARDOUR;
|
||||
using namespace Temporal;
|
||||
|
||||
/**** Handler ***/
|
||||
TempoMapImportHandler::TempoMapImportHandler (XMLTree const & source, Session & session) :
|
||||
|
@ -98,5 +99,7 @@ TempoMapImporter::_cancel_move ()
|
|||
void
|
||||
TempoMapImporter::_move ()
|
||||
{
|
||||
session.tempo_map().set_state (xml_tempo_map, Stateful::current_state_version);
|
||||
TempoMap::SharedPtr tmap (TempoMap::write_copy());
|
||||
tmap->set_state (xml_tempo_map, Stateful::current_state_version);
|
||||
TempoMap::update (tmap);
|
||||
}
|
||||
|
|
|
@ -215,7 +215,7 @@ out:
|
|||
double
|
||||
MidiClockTicker::one_ppqn_in_samples (samplepos_t transport_position) const
|
||||
{
|
||||
Tempo const & tempo (_session->tempo_map().metric_at (transport_position).tempo());
|
||||
Tempo const & tempo (TempoMap::use()->metric_at (transport_position).tempo());
|
||||
const double samples_per_quarter_note = tempo.samples_per_quarter_note (_session->nominal_sample_rate());
|
||||
return samples_per_quarter_note / 24.0;
|
||||
}
|
||||
|
|
|
@ -115,7 +115,7 @@ JACKSession::timebase_callback (jack_transport_state_t /*state*/,
|
|||
int /*new_position*/)
|
||||
{
|
||||
Temporal::BBT_Time bbt;
|
||||
TempoMap& tempo_map (_session->tempo_map());
|
||||
TempoMap::SharedPtr tempo_map (TempoMap::use());
|
||||
samplepos_t tf;
|
||||
|
||||
/* see commit msg for e2c26e1b9 and Session::start_locate() for
|
||||
|
@ -126,10 +126,10 @@ JACKSession::timebase_callback (jack_transport_state_t /*state*/,
|
|||
|
||||
/* BBT info */
|
||||
|
||||
TempoMetric metric (tempo_map.metric_at (tf));
|
||||
TempoMetric metric (tempo_map->metric_at (tf));
|
||||
|
||||
try {
|
||||
bbt = tempo_map.bbt_at (tf);
|
||||
bbt = tempo_map->bbt_at (tf);
|
||||
|
||||
pos->bar = bbt.bars;
|
||||
pos->beat = bbt.beats;
|
||||
|
|
|
@ -548,8 +548,8 @@ BasicUI::jump_by_seconds (double secs, LocateTransportDisposition ltd)
|
|||
void
|
||||
BasicUI::jump_by_bars (int bars, LocateTransportDisposition ltd)
|
||||
{
|
||||
TempoMap& tmap (session->tempo_map());
|
||||
Temporal::BBT_Time bbt (tmap.bbt_at (session->transport_sample()));
|
||||
TempoMap::SharedPtr tmap (TempoMap::use());
|
||||
Temporal::BBT_Time bbt (tmap->bbt_at (session->transport_sample()));
|
||||
|
||||
bars += bbt.bars;
|
||||
if (bars < 0) {
|
||||
|
|
|
@ -268,7 +268,7 @@ FaderPort8::periodic ()
|
|||
_timecode = Timecode::timecode_format_time(TC);
|
||||
|
||||
char buf[16];
|
||||
Temporal::BBT_Time BBT = session->tempo_map ().bbt_at (session->transport_sample ());
|
||||
Temporal::BBT_Time BBT = Temporal::TempoMap::use()->bbt_at (session->transport_sample ());
|
||||
snprintf (buf, sizeof (buf),
|
||||
" %02" PRIu32 "|%02" PRIu32 "|%02" PRIu32 "|%02" PRIu32,
|
||||
BBT.bars % 100, BBT.beats %100,
|
||||
|
|
|
@ -603,7 +603,7 @@ TrackMixLayout::update_clocks ()
|
|||
}
|
||||
|
||||
char buf[16];
|
||||
Temporal::BBT_Time BBT = session.tempo_map().bbt_at (pos);
|
||||
Temporal::BBT_Time BBT = Temporal::TempoMap::use()->bbt_at (pos);
|
||||
|
||||
#define BBT_BAR_CHAR "|"
|
||||
|
||||
|
|
|
@ -229,8 +229,10 @@ ArdourFeedback::observe_transport ()
|
|||
boost::bind<void> (TransportObserver (), this), event_loop ());
|
||||
sess.RecordStateChanged.connect (_transport_connections, MISSING_INVALIDATOR,
|
||||
boost::bind<void> (RecordStateObserver (), this), event_loop ());
|
||||
sess.tempo_map ().PropertyChanged.connect (_transport_connections, MISSING_INVALIDATOR,
|
||||
boost::bind<void> (TempoObserver (), this), event_loop ());
|
||||
|
||||
#warning NUTEMPO this is not right. the actual map can change. static signal?
|
||||
Temporal::TempoMap::use()->Changed.connect (_signal_connections, MISSING_INVALIDATOR,
|
||||
boost::bind<void> (TempoObserver (), this), event_loop ());
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -252,7 +254,7 @@ ArdourFeedback::observe_mixer ()
|
|||
|
||||
stripable->mute_control ()->Changed.connect (*it->second, MISSING_INVALIDATOR,
|
||||
boost::bind<void> (StripMuteObserver (), this, strip_id), event_loop ());
|
||||
|
||||
|
||||
observe_strip_plugins (strip_id, strip->plugins ());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -27,18 +27,21 @@ using namespace Temporal;
|
|||
double
|
||||
ArdourTransport::tempo () const
|
||||
{
|
||||
const Tempo& tempo (session ().tempo_map ().metric_at (0).tempo());
|
||||
const Tempo& tempo (TempoMap::use()->metric_at (0).tempo());
|
||||
return tempo.note_types_per_minute ();
|
||||
}
|
||||
|
||||
void
|
||||
ArdourTransport::set_tempo (double bpm)
|
||||
{
|
||||
bpm = std::max (0.01, bpm);
|
||||
TempoMap& tempo_map = session ().tempo_map ();
|
||||
Tempo tempo (bpm, tempo_map.metric_at (0).tempo().note_type ());
|
||||
#warning NUTEMPO need some API to do this
|
||||
// tempo_map.add_tempo (tempo, 0.0, 0, AudioTime);
|
||||
bpm = std::max (0.01, bpm);
|
||||
|
||||
TempoMap::SharedPtr tmap (TempoMap::write_copy());
|
||||
|
||||
Tempo tempo (bpm, tmap->metric_at (0).tempo().note_type ());
|
||||
|
||||
tmap->set_tempo (tempo, timepos_t());
|
||||
TempoMap::update (tmap);
|
||||
}
|
||||
|
||||
double
|
||||
|
|
|
@ -618,7 +618,7 @@ class LIBTEMPORAL_API TempoMap : public PBD::StatefulDestructible
|
|||
static SerializedRCUManager<TempoMap> _map_mgr;
|
||||
public:
|
||||
static void update_thread_tempo_map() { _tempo_map_p = _map_mgr.reader(); }
|
||||
static SharedPtr use() { return _tempo_map_p; }
|
||||
static SharedPtr use() { assert (_tempo_map_p); return _tempo_map_p; }
|
||||
static SharedPtr fetch() { update_thread_tempo_map(); return use(); }
|
||||
|
||||
static SharedPtr write_copy() { return _map_mgr.write_copy(); }
|
||||
|
@ -663,6 +663,7 @@ class LIBTEMPORAL_API TempoMap : public PBD::StatefulDestructible
|
|||
bool move_meter (MeterPoint const & point, timepos_t const & destination, bool push = false);
|
||||
|
||||
void set_time_domain (TimeDomain td);
|
||||
int set_state (XMLNode const&, int version);
|
||||
|
||||
/* END OF MODIFYING METHODS */
|
||||
|
||||
|
@ -764,7 +765,6 @@ class LIBTEMPORAL_API TempoMap : public PBD::StatefulDestructible
|
|||
PBD::Signal0<void> Changed;
|
||||
|
||||
XMLNode& get_state();
|
||||
int set_state (XMLNode const&, int version);
|
||||
|
||||
typedef boost::intrusive::member_hook<TempoPoint,boost::intrusive::list_member_hook<>, &TempoPoint::_tempo_hook> TempoHookOption;
|
||||
typedef boost::intrusive::member_hook<MeterPoint,boost::intrusive::list_member_hook<>, &MeterPoint::_meter_hook> MeterHookOption;
|
||||
|
|
Loading…
Reference in New Issue
Block a user