Browse Source

Update RtMidi

tags/1.9.8
falkTX 7 years ago
parent
commit
4df4ea4da4
2 changed files with 466 additions and 302 deletions
  1. +355
    -225
      source/modules/rtmidi/RtMidi.cpp
  2. +111
    -77
      source/modules/rtmidi/RtMidi.h

+ 355
- 225
source/modules/rtmidi/RtMidi.cpp
File diff suppressed because it is too large
View File


+ 111
- 77
source/modules/rtmidi/RtMidi.h View File

@@ -8,7 +8,7 @@
RtMidi WWW site: http://music.mcgill.ca/~gary/rtmidi/

RtMidi: realtime MIDI i/o C++ classes
Copyright (c) 2003-2014 Gary P. Scavone
Copyright (c) 2003-2017 Gary P. Scavone

Permission is hereby granted, free of charge, to any person
obtaining a copy of this software and associated documentation files
@@ -43,7 +43,8 @@
#ifndef RTMIDI_H
#define RTMIDI_H

#define RTMIDI_VERSION "2.1.0"
#define RTMIDI_DLL_PUBLIC
#define RTMIDI_VERSION "3.0.0"

#include <exception>
#include <iostream>
@@ -60,7 +61,7 @@
*/
/************************************************************************/

class RtMidiError : public std::exception
class RTMIDI_DLL_PUBLIC RtMidiError : public std::exception
{
public:
//! Defined RtMidiError types.
@@ -109,11 +110,11 @@ class RtMidiError : public std::exception
Note that class behaviour is undefined after a critical error (not
a warning) is reported.
*/
typedef void (*RtMidiErrorCallback)( RtMidiError::Type type, const std::string &errorText );
typedef void (*RtMidiErrorCallback)( RtMidiError::Type type, const std::string &errorText, void *userData );

class MidiApi;

class RtMidi
class RTMIDI_DLL_PUBLIC RtMidi
{
public:

@@ -139,10 +140,10 @@ class RtMidi
static void getCompiledApi( std::vector<RtMidi::Api> &apis ) throw();

//! Pure virtual openPort() function.
virtual void openPort( unsigned int portNumber = 0, const std::string portName = std::string( "RtMidi" ) ) = 0;
virtual void openPort( unsigned int portNumber = 0, const std::string &portName = std::string( "RtMidi" ) ) = 0;

//! Pure virtual openVirtualPort() function.
virtual void openVirtualPort( const std::string portName = std::string( "RtMidi" ) ) = 0;
virtual void openVirtualPort( const std::string &portName = std::string( "RtMidi" ) ) = 0;

//! Pure virtual getPortCount() function.
virtual unsigned int getPortCount() = 0;
@@ -154,6 +155,10 @@ class RtMidi
virtual void closePort( void ) = 0;

//! Returns true if a port is open and false if not.
/*!
Note that this only applies to connections made with the openPort()
function, not to virtual ports.
*/
virtual bool isPortOpen( void ) const = 0;

//! Set an error callback function to be invoked when an error has occured.
@@ -161,7 +166,7 @@ class RtMidi
The callback function will be called whenever an error has occured. It is best
to set the error callback function before opening a port.
*/
virtual void setErrorCallback( RtMidiErrorCallback errorCallback = NULL ) = 0;
virtual void setErrorCallback( RtMidiErrorCallback errorCallback = NULL, void *userData = 0 ) = 0;

protected:

@@ -185,7 +190,7 @@ class RtMidi
possible to open a virtual input port to which other MIDI software
clients can connect.

by Gary P. Scavone, 2003-2014.
by Gary P. Scavone, 2003-2017.
*/
/**********************************************************************/

@@ -203,7 +208,7 @@ class RtMidi
//
// **************************************************************** //

class RtMidiIn : public RtMidi
class RTMIDI_DLL_PUBLIC RtMidiIn : public RtMidi
{
public:

@@ -229,7 +234,7 @@ class RtMidiIn : public RtMidi
\param queueSizeLimit An optional size of the MIDI input queue can be specified.
*/
RtMidiIn( RtMidi::Api api=UNSPECIFIED,
const std::string clientName = std::string( "RtMidi Input Client"),
const std::string& clientName = "RtMidi Input Client",
unsigned int queueSizeLimit = 100 );

//! If a MIDI connection is still open, it will be closed by the destructor.
@@ -244,7 +249,7 @@ class RtMidiIn : public RtMidi
Otherwise, the default or first port found is opened.
\param portName An optional name for the application port that is used to connect to portId can be specified.
*/
void openPort( unsigned int portNumber = 0, const std::string portName = std::string( "RtMidi Input" ) );
void openPort( unsigned int portNumber = 0, const std::string &portName = std::string( "RtMidi Input" ) );

//! Create a virtual input port, with optional name, to allow software connections (OS X, JACK and ALSA only).
/*!
@@ -256,7 +261,7 @@ class RtMidiIn : public RtMidi
\param portName An optional name for the application port that is
used to connect to portId can be specified.
*/
void openVirtualPort( const std::string portName = std::string( "RtMidi Input" ) );
void openVirtualPort( const std::string &portName = std::string( "RtMidi Input" ) );

//! Set a callback function to be invoked for incoming MIDI messages.
/*!
@@ -282,6 +287,10 @@ class RtMidiIn : public RtMidi
void closePort( void );

//! Returns true if a port is open and false if not.
/*!
Note that this only applies to connections made with the openPort()
function, not to virtual ports.
*/
virtual bool isPortOpen() const;

//! Return the number of available MIDI input ports.
@@ -293,7 +302,8 @@ class RtMidiIn : public RtMidi
//! Return a string identifier for the specified MIDI input port number.
/*!
\return The name of the port with the given Id is returned.
\retval An empty string is returned if an invalid port specifier is provided.
\retval An empty string is returned if an invalid port specifier
is provided. User code should assume a UTF-8 encoding.
*/
std::string getPortName( unsigned int portNumber = 0 );

@@ -322,10 +332,10 @@ class RtMidiIn : public RtMidi
The callback function will be called whenever an error has occured. It is best
to set the error callback function before opening a port.
*/
virtual void setErrorCallback( RtMidiErrorCallback errorCallback = NULL );
virtual void setErrorCallback( RtMidiErrorCallback errorCallback = NULL, void *userData = 0 );

protected:
void openMidiApi( RtMidi::Api api, const std::string clientName, unsigned int queueSizeLimit );
void openMidiApi( RtMidi::Api api, const std::string &clientName, unsigned int queueSizeLimit );

};

@@ -341,11 +351,11 @@ class RtMidiIn : public RtMidi
OS-X, Linux ALSA and JACK MIDI APIs, it is also possible to open a
virtual port to which other MIDI software clients can connect.

by Gary P. Scavone, 2003-2014.
by Gary P. Scavone, 2003-2017.
*/
/**********************************************************************/

class RtMidiOut : public RtMidi
class RTMIDI_DLL_PUBLIC RtMidiOut : public RtMidi
{
public:

@@ -358,7 +368,7 @@ class RtMidiOut : public RtMidi
JACK (OS-X).
*/
RtMidiOut( RtMidi::Api api=UNSPECIFIED,
const std::string clientName = std::string( "RtMidi Output Client") );
const std::string& clientName = "RtMidi Output Client" );

//! The destructor closes any open MIDI connections.
~RtMidiOut( void ) throw();
@@ -373,12 +383,16 @@ class RtMidiOut : public RtMidi
exception is thrown if an error occurs while attempting to make
the port connection.
*/
void openPort( unsigned int portNumber = 0, const std::string portName = std::string( "RtMidi Output" ) );
void openPort( unsigned int portNumber = 0, const std::string &portName = std::string( "RtMidi Output" ) );

//! Close an open MIDI connection (if one exists).
void closePort( void );

//! Returns true if a port is open and false if not.
/*!
Note that this only applies to connections made with the openPort()
function, not to virtual ports.
*/
virtual bool isPortOpen() const;

//! Create a virtual output port, with optional name, to allow software connections (OS X, JACK and ALSA only).
@@ -390,14 +404,16 @@ class RtMidiOut : public RtMidi
An exception is thrown if an error occurs while attempting to
create the virtual port.
*/
void openVirtualPort( const std::string portName = std::string( "RtMidi Output" ) );
void openVirtualPort( const std::string &portName = std::string( "RtMidi Output" ) );

//! Return the number of available MIDI output ports.
unsigned int getPortCount( void );

//! Return a string identifier for the specified MIDI port type and number.
/*!
An empty string is returned if an invalid port specifier is provided.
\return The name of the port with the given Id is returned.
\retval An empty string is returned if an invalid port specifier
is provided. User code should assume a UTF-8 encoding.
*/
std::string getPortName( unsigned int portNumber = 0 );

@@ -406,17 +422,27 @@ class RtMidiOut : public RtMidi
An exception is thrown if an error occurs during output or an
output connection was not previously established.
*/
void sendMessage( std::vector<unsigned char> *message );
void sendMessage( const std::vector<unsigned char> *message );

//! Immediately send a single message out an open MIDI output port.
/*!
An exception is thrown if an error occurs during output or an
output connection was not previously established.

\param message A pointer to the MIDI message as raw bytes
\param size Length of the MIDI message in bytes
*/
void sendMessage( const unsigned char *message, size_t size );

//! Set an error callback function to be invoked when an error has occured.
/*!
The callback function will be called whenever an error has occured. It is best
to set the error callback function before opening a port.
*/
virtual void setErrorCallback( RtMidiErrorCallback errorCallback = NULL );
virtual void setErrorCallback( RtMidiErrorCallback errorCallback = NULL, void *userData = 0 );

protected:
void openMidiApi( RtMidi::Api api, const std::string clientName );
void openMidiApi( RtMidi::Api api, const std::string &clientName );
};


@@ -433,22 +459,22 @@ class RtMidiOut : public RtMidi
//
// **************************************************************** //

class MidiApi
class RTMIDI_DLL_PUBLIC MidiApi
{
public:

MidiApi();
virtual ~MidiApi();
virtual RtMidi::Api getCurrentApi( void ) = 0;
virtual void openPort( unsigned int portNumber, const std::string portName ) = 0;
virtual void openVirtualPort( const std::string portName ) = 0;
virtual void openPort( unsigned int portNumber, const std::string &portName ) = 0;
virtual void openVirtualPort( const std::string &portName ) = 0;
virtual void closePort( void ) = 0;

virtual unsigned int getPortCount( void ) = 0;
virtual std::string getPortName( unsigned int portNumber ) = 0;

inline bool isPortOpen() const { return connected_; }
void setErrorCallback( RtMidiErrorCallback errorCallback );
void setErrorCallback( RtMidiErrorCallback errorCallback, void *userData );

//! A basic error reporting function for RtMidi classes.
void error( RtMidiError::Type type, std::string errorString );
@@ -460,9 +486,11 @@ protected:
bool connected_;
std::string errorString_;
RtMidiErrorCallback errorCallback_;
bool firstErrorOccurred_;
void *errorCallbackUserData_;
};

class MidiInApi : public MidiApi
class RTMIDI_DLL_PUBLIC MidiInApi : public MidiApi
{
public:

@@ -477,6 +505,8 @@ class MidiInApi : public MidiApi
// messages. Each message represents one and only one MIDI message.
struct MidiMessage {
std::vector<unsigned char> bytes;

//! Time in seconds elapsed since the previous message
double timeStamp;

// Default constructor.
@@ -487,13 +517,16 @@ class MidiInApi : public MidiApi
struct MidiQueue {
unsigned int front;
unsigned int back;
unsigned int size;
unsigned int ringSize;
MidiMessage *ring;

// Default constructor.
MidiQueue()
:front(0), back(0), size(0), ringSize(0) {}
:front(0), back(0), ringSize(0), ring(0) {}
bool push(const MidiMessage&);
bool pop(std::vector<unsigned char>*, double*);
unsigned int size(unsigned int *back=0,
unsigned int *front=0);
};

// The RtMidiInData structure is used to pass private class data to
@@ -521,13 +554,13 @@ class MidiInApi : public MidiApi
RtMidiInData inputData_;
};

class MidiOutApi : public MidiApi
class RTMIDI_DLL_PUBLIC MidiOutApi : public MidiApi
{
public:

MidiOutApi( void );
virtual ~MidiOutApi( void );
virtual void sendMessage( std::vector<unsigned char> *message ) = 0;
virtual void sendMessage( const unsigned char *message, size_t size ) = 0;
};

// **************************************************************** //
@@ -537,8 +570,8 @@ class MidiOutApi : public MidiApi
// **************************************************************** //

inline RtMidi::Api RtMidiIn :: getCurrentApi( void ) throw() { return rtapi_->getCurrentApi(); }
inline void RtMidiIn :: openPort( unsigned int portNumber, const std::string portName ) { rtapi_->openPort( portNumber, portName ); }
inline void RtMidiIn :: openVirtualPort( const std::string portName ) { rtapi_->openVirtualPort( portName ); }
inline void RtMidiIn :: openPort( unsigned int portNumber, const std::string &portName ) { rtapi_->openPort( portNumber, portName ); }
inline void RtMidiIn :: openVirtualPort( const std::string &portName ) { rtapi_->openVirtualPort( portName ); }
inline void RtMidiIn :: closePort( void ) { rtapi_->closePort(); }
inline bool RtMidiIn :: isPortOpen() const { return rtapi_->isPortOpen(); }
inline void RtMidiIn :: setCallback( RtMidiCallback callback, void *userData ) { ((MidiInApi *)rtapi_)->setCallback( callback, userData ); }
@@ -547,17 +580,18 @@ inline unsigned int RtMidiIn :: getPortCount( void ) { return rtapi_->getPortCou
inline std::string RtMidiIn :: getPortName( unsigned int portNumber ) { return rtapi_->getPortName( portNumber ); }
inline void RtMidiIn :: ignoreTypes( bool midiSysex, bool midiTime, bool midiSense ) { ((MidiInApi *)rtapi_)->ignoreTypes( midiSysex, midiTime, midiSense ); }
inline double RtMidiIn :: getMessage( std::vector<unsigned char> *message ) { return ((MidiInApi *)rtapi_)->getMessage( message ); }
inline void RtMidiIn :: setErrorCallback( RtMidiErrorCallback errorCallback ) { rtapi_->setErrorCallback(errorCallback); }
inline void RtMidiIn :: setErrorCallback( RtMidiErrorCallback errorCallback, void *userData ) { rtapi_->setErrorCallback(errorCallback, userData); }

inline RtMidi::Api RtMidiOut :: getCurrentApi( void ) throw() { return rtapi_->getCurrentApi(); }
inline void RtMidiOut :: openPort( unsigned int portNumber, const std::string portName ) { rtapi_->openPort( portNumber, portName ); }
inline void RtMidiOut :: openVirtualPort( const std::string portName ) { rtapi_->openVirtualPort( portName ); }
inline void RtMidiOut :: openPort( unsigned int portNumber, const std::string &portName ) { rtapi_->openPort( portNumber, portName ); }
inline void RtMidiOut :: openVirtualPort( const std::string &portName ) { rtapi_->openVirtualPort( portName ); }
inline void RtMidiOut :: closePort( void ) { rtapi_->closePort(); }
inline bool RtMidiOut :: isPortOpen() const { return rtapi_->isPortOpen(); }
inline unsigned int RtMidiOut :: getPortCount( void ) { return rtapi_->getPortCount(); }
inline std::string RtMidiOut :: getPortName( unsigned int portNumber ) { return rtapi_->getPortName( portNumber ); }
inline void RtMidiOut :: sendMessage( std::vector<unsigned char> *message ) { ((MidiOutApi *)rtapi_)->sendMessage( message ); }
inline void RtMidiOut :: setErrorCallback( RtMidiErrorCallback errorCallback ) { rtapi_->setErrorCallback(errorCallback); }
inline void RtMidiOut :: sendMessage( const std::vector<unsigned char> *message ) { ((MidiOutApi *)rtapi_)->sendMessage( &message->at(0), message->size() ); }
inline void RtMidiOut :: sendMessage( const unsigned char *message, size_t size ) { ((MidiOutApi *)rtapi_)->sendMessage( message, size ); }
inline void RtMidiOut :: setErrorCallback( RtMidiErrorCallback errorCallback, void *userData ) { rtapi_->setErrorCallback(errorCallback, userData); }

// **************************************************************** //
//
@@ -574,11 +608,11 @@ inline void RtMidiOut :: setErrorCallback( RtMidiErrorCallback errorCallback ) {
class MidiInCore: public MidiInApi
{
public:
MidiInCore( const std::string clientName, unsigned int queueSizeLimit );
MidiInCore( const std::string &clientName, unsigned int queueSizeLimit );
~MidiInCore( void );
RtMidi::Api getCurrentApi( void ) { return RtMidi::MACOSX_CORE; };
void openPort( unsigned int portNumber, const std::string portName );
void openVirtualPort( const std::string portName );
void openPort( unsigned int portNumber, const std::string &portName );
void openVirtualPort( const std::string &portName );
void closePort( void );
unsigned int getPortCount( void );
std::string getPortName( unsigned int portNumber );
@@ -590,15 +624,15 @@ class MidiInCore: public MidiInApi
class MidiOutCore: public MidiOutApi
{
public:
MidiOutCore( const std::string clientName );
MidiOutCore( const std::string &clientName );
~MidiOutCore( void );
RtMidi::Api getCurrentApi( void ) { return RtMidi::MACOSX_CORE; };
void openPort( unsigned int portNumber, const std::string portName );
void openVirtualPort( const std::string portName );
void openPort( unsigned int portNumber, const std::string &portName );
void openVirtualPort( const std::string &portName );
void closePort( void );
unsigned int getPortCount( void );
std::string getPortName( unsigned int portNumber );
void sendMessage( std::vector<unsigned char> *message );
void sendMessage( const unsigned char *message, size_t size );

protected:
void initialize( const std::string& clientName );
@@ -611,11 +645,11 @@ class MidiOutCore: public MidiOutApi
class MidiInJack: public MidiInApi
{
public:
MidiInJack( const std::string clientName, unsigned int queueSizeLimit );
MidiInJack( const std::string &clientName, unsigned int queueSizeLimit );
~MidiInJack( void );
RtMidi::Api getCurrentApi( void ) { return RtMidi::UNIX_JACK; };
void openPort( unsigned int portNumber, const std::string portName );
void openVirtualPort( const std::string portName );
void openPort( unsigned int portNumber, const std::string &portName );
void openVirtualPort( const std::string &portName );
void closePort( void );
unsigned int getPortCount( void );
std::string getPortName( unsigned int portNumber );
@@ -630,15 +664,15 @@ class MidiInJack: public MidiInApi
class MidiOutJack: public MidiOutApi
{
public:
MidiOutJack( const std::string clientName );
MidiOutJack( const std::string &clientName );
~MidiOutJack( void );
RtMidi::Api getCurrentApi( void ) { return RtMidi::UNIX_JACK; };
void openPort( unsigned int portNumber, const std::string portName );
void openVirtualPort( const std::string portName );
void openPort( unsigned int portNumber, const std::string &portName );
void openVirtualPort( const std::string &portName );
void closePort( void );
unsigned int getPortCount( void );
std::string getPortName( unsigned int portNumber );
void sendMessage( std::vector<unsigned char> *message );
void sendMessage( const unsigned char *message, size_t size );

protected:
std::string clientName;
@@ -654,11 +688,11 @@ class MidiOutJack: public MidiOutApi
class MidiInAlsa: public MidiInApi
{
public:
MidiInAlsa( const std::string clientName, unsigned int queueSizeLimit );
MidiInAlsa( const std::string &clientName, unsigned int queueSizeLimit );
~MidiInAlsa( void );
RtMidi::Api getCurrentApi( void ) { return RtMidi::LINUX_ALSA; };
void openPort( unsigned int portNumber, const std::string portName );
void openVirtualPort( const std::string portName );
void openPort( unsigned int portNumber, const std::string &portName );
void openVirtualPort( const std::string &portName );
void closePort( void );
unsigned int getPortCount( void );
std::string getPortName( unsigned int portNumber );
@@ -670,15 +704,15 @@ class MidiInAlsa: public MidiInApi
class MidiOutAlsa: public MidiOutApi
{
public:
MidiOutAlsa( const std::string clientName );
MidiOutAlsa( const std::string &clientName );
~MidiOutAlsa( void );
RtMidi::Api getCurrentApi( void ) { return RtMidi::LINUX_ALSA; };
void openPort( unsigned int portNumber, const std::string portName );
void openVirtualPort( const std::string portName );
void openPort( unsigned int portNumber, const std::string &portName );
void openVirtualPort( const std::string &portName );
void closePort( void );
unsigned int getPortCount( void );
std::string getPortName( unsigned int portNumber );
void sendMessage( std::vector<unsigned char> *message );
void sendMessage( const unsigned char *message, size_t size );

protected:
void initialize( const std::string& clientName );
@@ -691,11 +725,11 @@ class MidiOutAlsa: public MidiOutApi
class MidiInWinMM: public MidiInApi
{
public:
MidiInWinMM( const std::string clientName, unsigned int queueSizeLimit );
MidiInWinMM( const std::string &clientName, unsigned int queueSizeLimit );
~MidiInWinMM( void );
RtMidi::Api getCurrentApi( void ) { return RtMidi::WINDOWS_MM; };
void openPort( unsigned int portNumber, const std::string portName );
void openVirtualPort( const std::string portName );
void openPort( unsigned int portNumber, const std::string &portName );
void openVirtualPort( const std::string &portName );
void closePort( void );
unsigned int getPortCount( void );
std::string getPortName( unsigned int portNumber );
@@ -707,15 +741,15 @@ class MidiInWinMM: public MidiInApi
class MidiOutWinMM: public MidiOutApi
{
public:
MidiOutWinMM( const std::string clientName );
MidiOutWinMM( const std::string &clientName );
~MidiOutWinMM( void );
RtMidi::Api getCurrentApi( void ) { return RtMidi::WINDOWS_MM; };
void openPort( unsigned int portNumber, const std::string portName );
void openVirtualPort( const std::string portName );
void openPort( unsigned int portNumber, const std::string &portName );
void openVirtualPort( const std::string &portName );
void closePort( void );
unsigned int getPortCount( void );
std::string getPortName( unsigned int portNumber );
void sendMessage( std::vector<unsigned char> *message );
void sendMessage( const unsigned char *message, size_t size );

protected:
void initialize( const std::string& clientName );
@@ -728,13 +762,13 @@ class MidiOutWinMM: public MidiOutApi
class MidiInDummy: public MidiInApi
{
public:
MidiInDummy( const std::string /*clientName*/, unsigned int queueSizeLimit ) : MidiInApi( queueSizeLimit ) { errorString_ = "MidiInDummy: This class provides no functionality."; error( RtMidiError::WARNING, errorString_ ); }
MidiInDummy( const std::string &/*clientName*/, unsigned int queueSizeLimit ) : MidiInApi( queueSizeLimit ) { errorString_ = "MidiInDummy: This class provides no functionality."; error( RtMidiError::WARNING, errorString_ ); }
RtMidi::Api getCurrentApi( void ) { return RtMidi::RTMIDI_DUMMY; }
void openPort( unsigned int /*portNumber*/, const std::string /*portName*/ ) {}
void openVirtualPort( const std::string /*portName*/ ) {}
void openPort( unsigned int /*portNumber*/, const std::string &/*portName*/ ) {}
void openVirtualPort( const std::string &/*portName*/ ) {}
void closePort( void ) {}
unsigned int getPortCount( void ) { return 0; }
std::string getPortName( unsigned int portNumber ) { return ""; }
std::string getPortName( unsigned int /*portNumber*/ ) { return ""; }

protected:
void initialize( const std::string& /*clientName*/ ) {}
@@ -743,14 +777,14 @@ class MidiInDummy: public MidiInApi
class MidiOutDummy: public MidiOutApi
{
public:
MidiOutDummy( const std::string /*clientName*/ ) { errorString_ = "MidiOutDummy: This class provides no functionality."; error( RtMidiError::WARNING, errorString_ ); }
MidiOutDummy( const std::string &/*clientName*/ ) { errorString_ = "MidiOutDummy: This class provides no functionality."; error( RtMidiError::WARNING, errorString_ ); }
RtMidi::Api getCurrentApi( void ) { return RtMidi::RTMIDI_DUMMY; }
void openPort( unsigned int /*portNumber*/, const std::string /*portName*/ ) {}
void openVirtualPort( const std::string /*portName*/ ) {}
void openPort( unsigned int /*portNumber*/, const std::string &/*portName*/ ) {}
void openVirtualPort( const std::string &/*portName*/ ) {}
void closePort( void ) {}
unsigned int getPortCount( void ) { return 0; }
std::string getPortName( unsigned int /*portNumber*/ ) { return ""; }
void sendMessage( std::vector<unsigned char> * /*message*/ ) {}
void sendMessage( const unsigned char * /*message*/, size_t /*size*/ ) {}

protected:
void initialize( const std::string& /*clientName*/ ) {}


Loading…
Cancel
Save