Unverified Commit 27e6779c authored by hwangjeff's avatar hwangjeff Committed by GitHub
Browse files

[fbsync] Style fixes (#1766)

Applies style fixes identified in fbcode.
parent b00bacf7
...@@ -94,7 +94,7 @@ if (BUILD_TORCHAUDIO_PYTHON_EXTENSION) ...@@ -94,7 +94,7 @@ if (BUILD_TORCHAUDIO_PYTHON_EXTENSION)
if(BUILD_SOX) if(BUILD_SOX)
list( list(
APPEND APPEND
EXTENSION_SOURCES EXTENSION_SOURCES
pybind/sox/effects.cpp pybind/sox/effects.cpp
pybind/sox/effects_chain.cpp pybind/sox/effects_chain.cpp
pybind/sox/io.cpp pybind/sox/io.cpp
......
...@@ -4,8 +4,7 @@ ...@@ -4,8 +4,7 @@
using namespace torchaudio::sox_utils; using namespace torchaudio::sox_utils;
namespace torchaudio { namespace torchaudio::sox_effects {
namespace sox_effects {
// Streaming decoding over file-like object is tricky because libsox operates on // Streaming decoding over file-like object is tricky because libsox operates on
// FILE pointer. The folloing is what `sox` and `play` commands do // FILE pointer. The folloing is what `sox` and `play` commands do
...@@ -28,12 +27,12 @@ namespace sox_effects { ...@@ -28,12 +27,12 @@ namespace sox_effects {
// fileobj. This will trick libsox as if it keeps reading from the FILE* // fileobj. This will trick libsox as if it keeps reading from the FILE*
// continuously. // continuously.
// For Step 2. see `fileobj_input_drain` function in effects_chain.cpp // For Step 2. see `fileobj_input_drain` function in effects_chain.cpp
std::tuple<torch::Tensor, int64_t> apply_effects_fileobj( auto apply_effects_fileobj(
py::object fileobj, py::object fileobj,
std::vector<std::vector<std::string>> effects, const std::vector<std::vector<std::string>>& effects,
c10::optional<bool> normalize, c10::optional<bool> normalize,
c10::optional<bool> channels_first, c10::optional<bool> channels_first,
c10::optional<std::string> format) { c10::optional<std::string> format) -> std::tuple<torch::Tensor, int64_t> {
// Prepare the buffer used throughout the lifecycle of SoxEffectChain. // Prepare the buffer used throughout the lifecycle of SoxEffectChain.
// //
// For certain format (such as FLAC), libsox keeps reading the content at // For certain format (such as FLAC), libsox keeps reading the content at
...@@ -115,5 +114,4 @@ std::tuple<torch::Tensor, int64_t> apply_effects_fileobj( ...@@ -115,5 +114,4 @@ std::tuple<torch::Tensor, int64_t> apply_effects_fileobj(
tensor, static_cast<int64_t>(chain.getOutputSampleRate())); tensor, static_cast<int64_t>(chain.getOutputSampleRate()));
} }
} // namespace sox_effects } // namespace torchaudio::sox_effects
} // namespace torchaudio
...@@ -3,17 +3,15 @@ ...@@ -3,17 +3,15 @@
#include <torch/extension.h> #include <torch/extension.h>
namespace torchaudio { namespace torchaudio::sox_effects {
namespace sox_effects {
std::tuple<torch::Tensor, int64_t> apply_effects_fileobj( auto apply_effects_fileobj(
py::object fileobj, py::object fileobj,
std::vector<std::vector<std::string>> effects, const std::vector<std::vector<std::string>>& effects,
c10::optional<bool> normalize, c10::optional<bool> normalize,
c10::optional<bool> channels_first, c10::optional<bool> channels_first,
c10::optional<std::string> format); c10::optional<std::string> format) -> std::tuple<torch::Tensor, int64_t>;
} // namespace sox_effects } // namespace torchaudio::sox_effects
} // namespace torchaudio
#endif #endif
...@@ -4,8 +4,7 @@ ...@@ -4,8 +4,7 @@
using namespace torchaudio::sox_utils; using namespace torchaudio::sox_utils;
namespace torchaudio { namespace torchaudio::sox_effects_chain {
namespace sox_effects_chain {
namespace { namespace {
...@@ -27,7 +26,8 @@ struct FileObjOutputPriv { ...@@ -27,7 +26,8 @@ struct FileObjOutputPriv {
/// Callback function to feed byte string /// Callback function to feed byte string
/// https://github.com/dmkrepo/libsox/blob/b9dd1a86e71bbd62221904e3e59dfaa9e5e72046/src/sox.h#L1268-L1278 /// https://github.com/dmkrepo/libsox/blob/b9dd1a86e71bbd62221904e3e59dfaa9e5e72046/src/sox.h#L1268-L1278
int fileobj_input_drain(sox_effect_t* effp, sox_sample_t* obuf, size_t* osamp) { auto fileobj_input_drain(sox_effect_t* effp, sox_sample_t* obuf, size_t* osamp)
-> int {
auto priv = static_cast<FileObjInputPriv*>(effp->priv); auto priv = static_cast<FileObjInputPriv*>(effp->priv);
auto sf = priv->sf; auto sf = priv->sf;
auto buffer = priv->buffer; auto buffer = priv->buffer;
...@@ -115,12 +115,12 @@ int fileobj_input_drain(sox_effect_t* effp, sox_sample_t* obuf, size_t* osamp) { ...@@ -115,12 +115,12 @@ int fileobj_input_drain(sox_effect_t* effp, sox_sample_t* obuf, size_t* osamp) {
return (priv->eof_reached && !*osamp) ? SOX_EOF : SOX_SUCCESS; return (priv->eof_reached && !*osamp) ? SOX_EOF : SOX_SUCCESS;
} }
int fileobj_output_flow( auto fileobj_output_flow(
sox_effect_t* effp, sox_effect_t* effp,
sox_sample_t const* ibuf, sox_sample_t const* ibuf,
sox_sample_t* obuf LSX_UNUSED, sox_sample_t* obuf LSX_UNUSED,
size_t* isamp, size_t* isamp,
size_t* osamp) { size_t* osamp) -> int {
*osamp = 0; *osamp = 0;
if (*isamp) { if (*isamp) {
auto priv = static_cast<FileObjOutputPriv*>(effp->priv); auto priv = static_cast<FileObjOutputPriv*>(effp->priv);
...@@ -128,7 +128,6 @@ int fileobj_output_flow( ...@@ -128,7 +128,6 @@ int fileobj_output_flow(
auto fp = static_cast<FILE*>(sf->fp); auto fp = static_cast<FILE*>(sf->fp);
auto fileobj = priv->fileobj; auto fileobj = priv->fileobj;
auto buffer = priv->buffer; auto buffer = priv->buffer;
auto buffer_size = priv->buffer_size;
// Encode chunk // Encode chunk
auto num_samples_written = sox_write(sf, ibuf, *isamp); auto num_samples_written = sox_write(sf, ibuf, *isamp);
...@@ -154,32 +153,32 @@ int fileobj_output_flow( ...@@ -154,32 +153,32 @@ int fileobj_output_flow(
return SOX_SUCCESS; return SOX_SUCCESS;
} }
sox_effect_handler_t* get_fileobj_input_handler() { auto get_fileobj_input_handler() -> sox_effect_handler_t* {
static sox_effect_handler_t handler{ static sox_effect_handler_t handler{
/*name=*/"input_fileobj_object", /*name=*/"input_fileobj_object",
/*usage=*/NULL, /*usage=*/nullptr,
/*flags=*/SOX_EFF_MCHAN, /*flags=*/SOX_EFF_MCHAN,
/*getopts=*/NULL, /*getopts=*/nullptr,
/*start=*/NULL, /*start=*/nullptr,
/*flow=*/NULL, /*flow=*/nullptr,
/*drain=*/fileobj_input_drain, /*drain=*/fileobj_input_drain,
/*stop=*/NULL, /*stop=*/nullptr,
/*kill=*/NULL, /*kill=*/nullptr,
/*priv_size=*/sizeof(FileObjInputPriv)}; /*priv_size=*/sizeof(FileObjInputPriv)};
return &handler; return &handler;
} }
sox_effect_handler_t* get_fileobj_output_handler() { auto get_fileobj_output_handler() -> sox_effect_handler_t* {
static sox_effect_handler_t handler{ static sox_effect_handler_t handler{
/*name=*/"output_fileobj_object", /*name=*/"output_fileobj_object",
/*usage=*/NULL, /*usage=*/nullptr,
/*flags=*/SOX_EFF_MCHAN, /*flags=*/SOX_EFF_MCHAN,
/*getopts=*/NULL, /*getopts=*/nullptr,
/*start=*/NULL, /*start=*/nullptr,
/*flow=*/fileobj_output_flow, /*flow=*/fileobj_output_flow,
/*drain=*/NULL, /*drain=*/nullptr,
/*stop=*/NULL, /*stop=*/nullptr,
/*kill=*/NULL, /*kill=*/nullptr,
/*priv_size=*/sizeof(FileObjOutputPriv)}; /*priv_size=*/sizeof(FileObjOutputPriv)};
return &handler; return &handler;
} }
...@@ -225,5 +224,4 @@ void SoxEffectsChainPyBind::addOutputFileObj( ...@@ -225,5 +224,4 @@ void SoxEffectsChainPyBind::addOutputFileObj(
} }
} }
} // namespace sox_effects_chain } // namespace torchaudio::sox_effects_chain
} // namespace torchaudio
...@@ -4,8 +4,7 @@ ...@@ -4,8 +4,7 @@
#include <torch/extension.h> #include <torch/extension.h>
#include <torchaudio/csrc/sox/effects_chain.h> #include <torchaudio/csrc/sox/effects_chain.h>
namespace torchaudio { namespace torchaudio::sox_effects_chain {
namespace sox_effects_chain {
class SoxEffectsChainPyBind : public SoxEffectsChain { class SoxEffectsChainPyBind : public SoxEffectsChain {
using SoxEffectsChain::SoxEffectsChain; using SoxEffectsChain::SoxEffectsChain;
...@@ -24,7 +23,6 @@ class SoxEffectsChainPyBind : public SoxEffectsChain { ...@@ -24,7 +23,6 @@ class SoxEffectsChainPyBind : public SoxEffectsChain {
py::object* fileobj); py::object* fileobj);
}; };
} // namespace sox_effects_chain } // namespace torchaudio::sox_effects_chain
} // namespace torchaudio
#endif #endif
...@@ -5,14 +5,14 @@ ...@@ -5,14 +5,14 @@
#include <torchaudio/csrc/sox/io.h> #include <torchaudio/csrc/sox/io.h>
#include <torchaudio/csrc/sox/types.h> #include <torchaudio/csrc/sox/types.h>
#include <utility>
using namespace torchaudio::sox_utils; using namespace torchaudio::sox_utils;
namespace torchaudio { namespace torchaudio::sox_io {
namespace sox_io {
std::tuple<int64_t, int64_t, int64_t, int64_t, std::string> get_info_fileobj( auto get_info_fileobj(py::object fileobj, c10::optional<std::string> format)
py::object fileobj, -> std::tuple<int64_t, int64_t, int64_t, int64_t, std::string> {
c10::optional<std::string> format) {
// Prepare in-memory file object // Prepare in-memory file object
// When libsox opens a file, it also reads the header. // When libsox opens a file, it also reads the header.
// When opening a file there are two functions that might touch FILE* (and the // When opening a file there are two functions that might touch FILE* (and the
...@@ -71,16 +71,20 @@ std::tuple<int64_t, int64_t, int64_t, int64_t, std::string> get_info_fileobj( ...@@ -71,16 +71,20 @@ std::tuple<int64_t, int64_t, int64_t, int64_t, std::string> get_info_fileobj(
get_encoding(sf->encoding.encoding)); get_encoding(sf->encoding.encoding));
} }
std::tuple<torch::Tensor, int64_t> load_audio_fileobj( auto load_audio_fileobj(
py::object fileobj, py::object fileobj,
c10::optional<int64_t> frame_offset, c10::optional<int64_t> frame_offset,
c10::optional<int64_t> num_frames, c10::optional<int64_t> num_frames,
c10::optional<bool> normalize, c10::optional<bool> normalize,
c10::optional<bool> channels_first, c10::optional<bool> channels_first,
c10::optional<std::string> format) { c10::optional<std::string> format) -> std::tuple<torch::Tensor, int64_t> {
auto effects = get_effects(frame_offset, num_frames); auto effects = get_effects(frame_offset, num_frames);
return torchaudio::sox_effects::apply_effects_fileobj( return torchaudio::sox_effects::apply_effects_fileobj(
fileobj, effects, normalize, channels_first, format); std::move(fileobj),
effects,
normalize,
channels_first,
std::move(format));
} }
namespace { namespace {
...@@ -94,8 +98,8 @@ struct AutoReleaseBuffer { ...@@ -94,8 +98,8 @@ struct AutoReleaseBuffer {
AutoReleaseBuffer() : ptr(nullptr), size(0) {} AutoReleaseBuffer() : ptr(nullptr), size(0) {}
AutoReleaseBuffer(const AutoReleaseBuffer& other) = delete; AutoReleaseBuffer(const AutoReleaseBuffer& other) = delete;
AutoReleaseBuffer(AutoReleaseBuffer&& other) = delete; AutoReleaseBuffer(AutoReleaseBuffer&& other) = delete;
AutoReleaseBuffer& operator=(const AutoReleaseBuffer& other) = delete; auto operator=(const AutoReleaseBuffer& other) -> AutoReleaseBuffer& = delete;
AutoReleaseBuffer& operator=(AutoReleaseBuffer&& other) = delete; auto operator=(AutoReleaseBuffer&& other) -> AutoReleaseBuffer& = delete;
~AutoReleaseBuffer() { ~AutoReleaseBuffer() {
if (ptr) { if (ptr) {
free(ptr); free(ptr);
...@@ -148,7 +152,11 @@ void save_audio_fileobj( ...@@ -148,7 +152,11 @@ void save_audio_fileobj(
const auto signal_info = const auto signal_info =
get_signalinfo(&tensor, sample_rate, filetype, channels_first); get_signalinfo(&tensor, sample_rate, filetype, channels_first);
const auto encoding_info = get_encodinginfo_for_save( const auto encoding_info = get_encodinginfo_for_save(
filetype, tensor.dtype(), compression, encoding, bits_per_sample); filetype,
tensor.dtype(),
compression,
std::move(encoding),
bits_per_sample);
AutoReleaseBuffer buffer; AutoReleaseBuffer buffer;
...@@ -179,5 +187,4 @@ void save_audio_fileobj( ...@@ -179,5 +187,4 @@ void save_audio_fileobj(
fileobj.attr("write")(py::bytes(buffer.ptr, buffer.size)); fileobj.attr("write")(py::bytes(buffer.ptr, buffer.size));
} }
} // namespace sox_io } // namespace torchaudio::sox_io
} // namespace torchaudio
...@@ -3,20 +3,18 @@ ...@@ -3,20 +3,18 @@
#include <torch/extension.h> #include <torch/extension.h>
namespace torchaudio { namespace torchaudio::sox_io {
namespace sox_io {
std::tuple<int64_t, int64_t, int64_t, int64_t, std::string> get_info_fileobj( auto get_info_fileobj(py::object fileobj, c10::optional<std::string> format)
py::object fileobj, -> std::tuple<int64_t, int64_t, int64_t, int64_t, std::string>;
c10::optional<std::string> format);
std::tuple<torch::Tensor, int64_t> load_audio_fileobj( auto load_audio_fileobj(
py::object fileobj, py::object fileobj,
c10::optional<int64_t> frame_offset, c10::optional<int64_t> frame_offset,
c10::optional<int64_t> num_frames, c10::optional<int64_t> num_frames,
c10::optional<bool> normalize, c10::optional<bool> normalize,
c10::optional<bool> channels_first, c10::optional<bool> channels_first,
c10::optional<std::string> format); c10::optional<std::string> format) -> std::tuple<torch::Tensor, int64_t>;
void save_audio_fileobj( void save_audio_fileobj(
py::object fileobj, py::object fileobj,
...@@ -28,7 +26,6 @@ void save_audio_fileobj( ...@@ -28,7 +26,6 @@ void save_audio_fileobj(
c10::optional<std::string> encoding, c10::optional<std::string> encoding,
c10::optional<int64_t> bits_per_sample); c10::optional<int64_t> bits_per_sample);
} // namespace sox_io } // namespace torchaudio::sox_io
} // namespace torchaudio
#endif #endif
#include <torchaudio/csrc/pybind/sox/utils.h> #include <torchaudio/csrc/pybind/sox/utils.h>
namespace torchaudio { namespace torchaudio::sox_utils {
namespace sox_utils {
uint64_t read_fileobj(py::object* fileobj, const uint64_t size, char* buffer) { auto read_fileobj(py::object* fileobj, const uint64_t size, char* buffer)
-> uint64_t {
uint64_t num_read = 0; uint64_t num_read = 0;
while (num_read < size) { while (num_read < size) {
auto request = size - num_read; auto request = size - num_read;
...@@ -28,5 +28,4 @@ uint64_t read_fileobj(py::object* fileobj, const uint64_t size, char* buffer) { ...@@ -28,5 +28,4 @@ uint64_t read_fileobj(py::object* fileobj, const uint64_t size, char* buffer) {
return num_read; return num_read;
} }
} // namespace sox_utils } // namespace torchaudio::sox_utils
} // namespace torchaudio
...@@ -3,12 +3,10 @@ ...@@ -3,12 +3,10 @@
#include <torch/extension.h> #include <torch/extension.h>
namespace torchaudio { namespace torchaudio::sox_utils {
namespace sox_utils {
uint64_t read_fileobj(py::object* fileobj, uint64_t size, char* buffer); auto read_fileobj(py::object* fileobj, uint64_t size, char* buffer) -> uint64_t;
} // namespace sox_utils } // namespace torchaudio::sox_utils
} // namespace torchaudio
#endif #endif
...@@ -5,8 +5,7 @@ ...@@ -5,8 +5,7 @@
using namespace torchaudio::sox_utils; using namespace torchaudio::sox_utils;
namespace torchaudio { namespace torchaudio::sox_effects {
namespace sox_effects {
namespace { namespace {
...@@ -25,6 +24,7 @@ void initialize_sox_effects() { ...@@ -25,6 +24,7 @@ void initialize_sox_effects() {
throw std::runtime_error("Failed to initialize sox effects."); throw std::runtime_error("Failed to initialize sox effects.");
}; };
SOX_RESOURCE_STATE = Initialized; SOX_RESOURCE_STATE = Initialized;
break;
case Initialized: case Initialized:
break; break;
case ShutDown: case ShutDown:
...@@ -45,16 +45,17 @@ void shutdown_sox_effects() { ...@@ -45,16 +45,17 @@ void shutdown_sox_effects() {
throw std::runtime_error("Failed to initialize sox effects."); throw std::runtime_error("Failed to initialize sox effects.");
}; };
SOX_RESOURCE_STATE = ShutDown; SOX_RESOURCE_STATE = ShutDown;
break;
case ShutDown: case ShutDown:
break; break;
} }
} }
std::tuple<torch::Tensor, int64_t> apply_effects_tensor( auto apply_effects_tensor(
torch::Tensor waveform, torch::Tensor waveform,
int64_t sample_rate, int64_t sample_rate,
std::vector<std::vector<std::string>> effects, const std::vector<std::vector<std::string>>& effects,
bool channels_first) { bool channels_first) -> std::tuple<torch::Tensor, int64_t> {
validate_input_tensor(waveform); validate_input_tensor(waveform);
// Create SoxEffectsChain // Create SoxEffectsChain
...@@ -81,19 +82,20 @@ std::tuple<torch::Tensor, int64_t> apply_effects_tensor( ...@@ -81,19 +82,20 @@ std::tuple<torch::Tensor, int64_t> apply_effects_tensor(
/*num_samples=*/out_buffer.size(), /*num_samples=*/out_buffer.size(),
/*num_channels=*/chain.getOutputNumChannels(), /*num_channels=*/chain.getOutputNumChannels(),
dtype, dtype,
/*noramlize=*/false, /*normalize=*/false,
channels_first); channels_first);
return std::tuple<torch::Tensor, int64_t>( return std::tuple<torch::Tensor, int64_t>(
out_tensor, chain.getOutputSampleRate()); out_tensor, chain.getOutputSampleRate());
} }
std::tuple<torch::Tensor, int64_t> apply_effects_file( auto apply_effects_file(
const std::string path, const std::string& path,
std::vector<std::vector<std::string>> effects, const std::vector<std::vector<std::string>>& effects,
c10::optional<bool> normalize, c10::optional<bool> normalize,
c10::optional<bool> channels_first, c10::optional<bool> channels_first,
const c10::optional<std::string>& format) { const c10::optional<std::string>& format)
-> std::tuple<torch::Tensor, int64_t> {
// Open input file // Open input file
SoxFormat sf(sox_open_read( SoxFormat sf(sox_open_read(
path.c_str(), path.c_str(),
...@@ -150,5 +152,4 @@ TORCH_LIBRARY_FRAGMENT(torchaudio, m) { ...@@ -150,5 +152,4 @@ TORCH_LIBRARY_FRAGMENT(torchaudio, m) {
&torchaudio::sox_effects::apply_effects_file); &torchaudio::sox_effects::apply_effects_file);
} }
} // namespace sox_effects } // namespace torchaudio::sox_effects
} // namespace torchaudio
...@@ -4,27 +4,26 @@ ...@@ -4,27 +4,26 @@
#include <torch/script.h> #include <torch/script.h>
#include <torchaudio/csrc/sox/utils.h> #include <torchaudio/csrc/sox/utils.h>
namespace torchaudio { namespace torchaudio::sox_effects {
namespace sox_effects {
void initialize_sox_effects(); void initialize_sox_effects();
void shutdown_sox_effects(); void shutdown_sox_effects();
std::tuple<torch::Tensor, int64_t> apply_effects_tensor( auto apply_effects_tensor(
torch::Tensor waveform, torch::Tensor waveform,
int64_t sample_rate, int64_t sample_rate,
std::vector<std::vector<std::string>> effects, const std::vector<std::vector<std::string>>& effects,
bool channels_first); bool channels_first) -> std::tuple<torch::Tensor, int64_t>;
std::tuple<torch::Tensor, int64_t> apply_effects_file( auto apply_effects_file(
const std::string path, const std::string& path,
std::vector<std::vector<std::string>> effects, const std::vector<std::vector<std::string>>& effects,
c10::optional<bool> normalize, c10::optional<bool> normalize,
c10::optional<bool> channels_first, c10::optional<bool> channels_first,
const c10::optional<std::string>& format); const c10::optional<std::string>& format)
-> std::tuple<torch::Tensor, int64_t>;
} // namespace sox_effects } // namespace torchaudio::sox_effects
} // namespace torchaudio
#endif #endif
...@@ -185,7 +185,7 @@ SoxEffect::operator sox_effect_t*() const { ...@@ -185,7 +185,7 @@ SoxEffect::operator sox_effect_t*() const {
return se_; return se_;
} }
sox_effect_t* SoxEffect::operator->() noexcept { auto SoxEffect::operator->() noexcept -> sox_effect_t* {
return se_; return se_;
} }
......
...@@ -14,11 +14,11 @@ struct SoxEffect { ...@@ -14,11 +14,11 @@ struct SoxEffect {
explicit SoxEffect(sox_effect_t* se) noexcept; explicit SoxEffect(sox_effect_t* se) noexcept;
SoxEffect(const SoxEffect& other) = delete; SoxEffect(const SoxEffect& other) = delete;
SoxEffect(const SoxEffect&& other) = delete; SoxEffect(const SoxEffect&& other) = delete;
SoxEffect& operator=(const SoxEffect& other) = delete; auto operator=(const SoxEffect& other) -> SoxEffect& = delete;
SoxEffect& operator=(SoxEffect&& other) = delete; auto operator=(SoxEffect&& other) -> SoxEffect& = delete;
~SoxEffect(); ~SoxEffect();
operator sox_effect_t*() const; operator sox_effect_t*() const;
sox_effect_t* operator->() noexcept; auto operator->() noexcept -> sox_effect_t*;
private: private:
sox_effect_t* se_; sox_effect_t* se_;
......
...@@ -7,9 +7,10 @@ ...@@ -7,9 +7,10 @@
namespace torchaudio { namespace torchaudio {
namespace sox_io { namespace sox_io {
std::vector<std::vector<std::string>> get_effects( auto get_effects(
const c10::optional<int64_t>& frame_offset, const c10::optional<int64_t>& frame_offset,
const c10::optional<int64_t>& num_frames); const c10::optional<int64_t>& num_frames)
-> std::vector<std::vector<std::string>>;
std::tuple<int64_t, int64_t, int64_t, int64_t, std::string> get_info_file( std::tuple<int64_t, int64_t, int64_t, int64_t, std::string> get_info_file(
const std::string& path, const std::string& path,
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment