code:
#include <string.h>
#include <math.h>
#include <string>
#include <random>
#include <complex>

#include "fft.hh"
#include "wavdata.hh"

#include <assert.h>

#include "config.h"

using std::string;
using std::vector;
using std::complex;
using std::min;

namespace Params
{
  static size_t frame_size   = 1024;
  static int frames_per_bit  = 4;
  static int bands_per_frame = 30;
  static int max_band        = 100;
  static int min_band        = 20;
  static double water_delta  = 0.015; // strength of the watermark
  static double pre_scale    = 0.95;  // rescale the signal to avoid clipping after watermark is added
}

void
print_usage()
{
  printf ("usage: audiowmark <command> [ <args>... ]\n");
  printf ("\n");
  printf ("Commands:\n");
  printf ("  * create a watermarked wav file with a message\n");
  printf ("    audiowmark add <input_wav> <watermarked_wav> <message_hex>\n");
  printf ("\n");
  printf ("  * blind decoding (retrieve message without original file)\n");
  printf ("    audiowmark get <watermarked_wav>\n");
  printf ("\n");
  printf ("  * compute bit error rate for blind decoding\n");
  printf ("    audiowmark cmp <watermarked_wav> <message_hex>\n");
  printf ("\n");
  printf ("  * retrieve message with original file\n");
  printf ("    audiowmark get-delta <input_wav> <watermarked_wav>\n");
  printf ("\n");
  printf ("  * compute bit error rate for decoding with original file\n");
  printf ("    audiowmark cmp-delta <input_wav> <watermarked_wav> <message_hex>\n");
  printf ("\n");
  printf ("Global options:\n");
  printf ("  --frame-size          frame size (must be power of 2)     [%zd]\n", Params::frame_size);
  printf ("  --frames-per-bit      number of frames per bit            [%d]\n",  Params::frames_per_bit);
  printf ("  --water-delta         set watermarking delta              [%.4f]\n", Params::water_delta);
  printf ("  --pre-scale           set scaling used for normalization  [%.3f]\n", Params::pre_scale);
}

static bool
check_arg (uint         argc,
           char        *argv[],
           uint        *nth,
           const char  *opt,		    /* for example: --foo */
           const char **opt_arg = nullptr)  /* if foo needs an argument, pass a pointer to get the argument */
{
  assert (opt != nullptr);
  assert (*nth < argc);

  const char *arg = argv[*nth];
  if (!arg)
    return false;

  uint opt_len = strlen (opt);
  if (strcmp (arg, opt) == 0)
    {
      if (opt_arg && *nth + 1 < argc)	  /* match foo option with argument: --foo bar */
        {
          argv[(*nth)++] = nullptr;
          *opt_arg = argv[*nth];
          argv[*nth] = nullptr;
          return true;
        }
      else if (!opt_arg)		  /* match foo option without argument: --foo */
        {
          argv[*nth] = nullptr;
          return true;
        }
      /* fall through to error message */
    }
  else if (strncmp (arg, opt, opt_len) == 0 && arg[opt_len] == '=')
    {
      if (opt_arg)			  /* match foo option with argument: --foo=bar */
        {
          *opt_arg = arg + opt_len + 1;
          argv[*nth] = nullptr;
          return true;
        }
      /* fall through to error message */
    }
  else
    return false;

  print_usage();
  exit (1);
}

void
parse_options (int   *argc_p,
               char **argv_p[])
{
  uint argc = *argc_p;
  char **argv = *argv_p;
  unsigned int i, e;

  for (i = 1; i < argc; i++)
    {
      const char *opt_arg;
      if (strcmp (argv[i], "--help") == 0 ||
          strcmp (argv[i], "-h") == 0)
	{
	  print_usage();
	  exit (0);
	}
      else if (strcmp (argv[i], "--version") == 0 || strcmp (argv[i], "-v") == 0)
	{
	  printf ("audiowmark %s\n", VERSION);
	  exit (0);
	}
      else if (check_arg (argc, argv, &i, "--frame-size", &opt_arg))
	{
          Params::frame_size = atoi (opt_arg);
	}
      else if (check_arg (argc, argv, &i, "--frames-per-bit", &opt_arg))
	{
          Params::frames_per_bit = atoi (opt_arg);
	}
      else if (check_arg (argc, argv, &i, "--water-delta", &opt_arg))
	{
          Params::water_delta = atof (opt_arg);
	}
      else if (check_arg (argc, argv, &i, "--pre-scale", &opt_arg))
	{
          Params::pre_scale = atof (opt_arg);
	}
    }

  /* resort argc/argv */
  e = 1;
  for (i = 1; i < argc; i++)
    if (argv[i])
      {
        argv[e++] = argv[i];
        if (i >= e)
          argv[i] = nullptr;
      }
  *argc_p = e;
}

inline double
window_cos (double x) /* von Hann window */
{
  if (fabs (x) > 1)
    return 0;
  return 0.5 * cos (x * M_PI) + 0.5;
}

inline double
window_hamming (double x) /* sharp (rectangle) cutoffs at boundaries */
{
  if (fabs (x) > 1)
    return 0;

  return 0.54 + 0.46 * cos (M_PI * x);
}

int
frame_count (WavData& wav_data)
{
  return (wav_data.n_values() / wav_data.n_channels() + (Params::frame_size - 1)) / Params::frame_size;
}

/*
 * get one audio frame, Params::frame_size samples if available
 *
 * in case of stereo: deinterleave
 */
vector<float>
get_frame (WavData& wav_data, int f, int ch)
{
  auto& samples = wav_data.samples();

  vector<float> result;

  size_t pos = (f * Params::frame_size) * wav_data.n_channels() + ch;
  for (size_t x = 0; x < Params::frame_size; x++)
    {
      if (pos < samples.size())
        result.push_back (samples[pos]);

      pos += wav_data.n_channels();
    }
  return result;
}

void
get_up_down (int f, vector<int>& up, vector<int>& down)
{
  vector<int> used (Params::frame_size / 2);
  std::mt19937_64 rng;

  // use per frame random seed, may want to have cryptographically secure algorithm
  rng.seed (f);

  auto choose_bands = [&used, &rng] (vector<int>& bands) {
    while (bands.size() < Params::bands_per_frame)
      {
        int p = rng() % (Params::max_band - Params::min_band) + Params::min_band;
        if (!used[p])
          {
            bands.push_back (p);
            used[p] = 1;
          }
      }
  };
  choose_bands (up);
  choose_bands (down);
}

static unsigned char
from_hex_nibble (char c)
{
  int uc = (unsigned char)c;

  if (uc >= '0' && uc <= '9') return uc - (unsigned char)'0';
  if (uc >= 'a' && uc <= 'f') return uc + 10 - (unsigned char)'a';
  if (uc >= 'A' && uc <= 'F') return uc + 10 - (unsigned char)'A';

  return 16;	// error
}

vector<int>
bit_str_to_vec (const string& bits)
{
  vector<int> bitvec;
  for (auto nibble : bits)
    {
      unsigned char c = from_hex_nibble (nibble);
      if (c >= 16)
        return vector<int>(); // error

      bitvec.push_back ((c & 8) > 0);
      bitvec.push_back ((c & 4) > 0);
      bitvec.push_back ((c & 2) > 0);
      bitvec.push_back ((c & 1) > 0);
    }
  return bitvec;
}

string
bit_vec_to_str (const vector<int>& bit_vec)
{
  string bit_str;

  for (size_t pos = 0; pos + 3 < bit_vec.size(); pos += 4) // convert only groups of 4 bits
    {
      int nibble = 0;
      for (int j = 0; j < 4; j++)
        {
          if (bit_vec[pos + j])
            {
              // j == 0 has the highest value, then 1, 2, 3 (lowest)
              nibble |= 1 << (3 - j);
            }
        }
      const char *to_hex = "0123456789abcdef";
      bit_str += to_hex[nibble];
    }
  return bit_str;
}

vector<float>
watermark_frame (const vector<float>& input_frame, int f, int data_bit)
{
  vector<float> frame = input_frame;

  /* windowing */
  double window_weight = 0;
  for (size_t i = 0; i < frame.size(); i++)
    {
      const double fsize_2 = frame.size() / 2.0;
      // const double win =  window_cos ((i - fsize_2) / fsize_2);
      const double win = window_hamming ((i - fsize_2) / fsize_2);
      //const double win = 1;
      frame[i] *= win;
      window_weight += win;
    }

  /* to get normalized fft output corrected by window weight */
  for (size_t i = 0; i < frame.size(); i++)
    frame[i] *= 2.0 / window_weight;

  /* FFT transform */
  vector<complex<float>> fft_out = fft (frame);

  vector<complex<float>> fft_delta_spect (fft_out.size());

  vector<int> up;
  vector<int> down;
  get_up_down (f, up, down);

  const double  data_bit_sign = data_bit > 0 ? 1 : -1;
  for (auto u : up)
    {
      /*
       * for up bands, we want do use [for a 1 bit]  (pow (mag, 1 - water_delta))
       *
       * this actually increases the amount of energy because mag is less than 1.0
       */
      const float mag_factor = pow (abs (fft_out[u]), -Params::water_delta * data_bit_sign);

      fft_delta_spect[u] = fft_out[u] * (mag_factor - 1);
    }
  for (auto d : down)
    {
      /*
       * for down bands, we want do use [for a 1 bit]   (pow (mag, 1 + water_delta))
       *
       * this actually decreases the amount of energy because mag is less than 1.0
       */
      const float mag_factor = pow (abs (fft_out[d]), Params::water_delta * data_bit_sign);

      fft_delta_spect[d] = fft_out[d] * (mag_factor - 1);
    }

  /* add watermark to output frame */
  vector<float> fft_delta_out = ifft (fft_delta_spect);

  vector<float> synth_window (Params::frame_size);
  for (size_t i = 0; i < Params::frame_size; i++)
    {
      const double threshold = 0.2;

      // triangular basic window
      const double tri = min (1.0 - fabs (double (2 * i)/Params::frame_size - 1.0), threshold) / threshold;

      // cosine
      synth_window[i] = (cos (tri*M_PI+M_PI)+1) * 0.5;
    }

  vector<float> new_frame = input_frame;
  for (size_t i = 0; i < frame.size(); i++)
    new_frame[i] += fft_delta_out[i] * synth_window[i];
  return new_frame;
}

int
add_watermark (const string& infile, const string& outfile, const string& bits)
{
  auto bitvec = bit_str_to_vec (bits);
  if (bitvec.empty())
    {
      fprintf (stderr, "audiowmark: cannot parse bits %s\n", bits.c_str());
      return 1;
    }

  printf ("loading %s\n", infile.c_str());

  WavData wav_data;
  if (!wav_data.load (infile))
    {
      fprintf (stderr, "audiowmark: error loading %s: %s\n", infile.c_str(), wav_data.error_blurb());
      return 1;
    }
  vector<float> out_signal (wav_data.n_values());
  printf ("channels: %d, samples: %zd, mix_freq: %f\n", wav_data.n_channels(), wav_data.n_values(), wav_data.mix_freq());

  for (int f = 0; f < frame_count (wav_data); f++)
    {
      for (int ch = 0; ch < wav_data.n_channels(); ch++)
        {
          vector<float> frame = get_frame (wav_data, f, ch);
          vector<float> new_frame;

          if (frame.size() == Params::frame_size)
            {
              const int data_bit = bitvec[(f / Params::frames_per_bit) % bitvec.size()];

              new_frame = watermark_frame (frame, f, data_bit);
            }
          else
            {
              new_frame = frame;
            }
          for (size_t i = 0; i < new_frame.size(); i++)
            out_signal[(f * Params::frame_size + i) * wav_data.n_channels() + ch] = new_frame[i] * Params::pre_scale;
        }
    }

  bool clipping_warning = false;
  for (auto value : out_signal)
    {
      if (fabs (value) >= 1.0 && !clipping_warning)
        {
          fprintf (stderr, "audiowmark: warning: clipping occured in watermarked audio signal\n");
          clipping_warning = true;
        }
    }

  WavData out_wav_data (out_signal, wav_data.n_channels(), wav_data.mix_freq(), wav_data.bit_depth());
  if (!out_wav_data.save (outfile))
    {
      fprintf (stderr, "audiowmark: error saving %s: %s\n", outfile.c_str(), out_wav_data.error_blurb());
      return 1;
    }
  return 0;
}

int
get_watermark (const string& infile, const string& orig_pattern)
{
  WavData wav_data;
  if (!wav_data.load (infile))
    {
      fprintf (stderr, "audiowmark: error loading %s: %s\n", infile.c_str(), wav_data.error_blurb());
      return 1;
    }
  vector<int> bit_vec;

  double umag = 0, dmag = 0;
  for (int f = 0; f < frame_count (wav_data); f++)
    {
      for (int ch = 0; ch < wav_data.n_channels(); ch++)
        {
          vector<float> frame = get_frame (wav_data, f, ch);
          if (frame.size() == Params::frame_size)
            {
              /* windowing */
              double window_weight = 0;
              for (size_t i = 0; i < frame.size(); i++)
                {
                  const double fsize_2 = frame.size() / 2.0;
                  // const double win =  window_cos ((i - fsize_2) / fsize_2);
                  const double win = window_hamming ((i - fsize_2) / fsize_2);
                  //const double win = 1;
                  frame[i] *= win;
                  window_weight += win;
                }

              /* to get normalized fft output corrected by window weight */
              for (size_t i = 0; i < frame.size(); i++)
                frame[i] *= 2.0 / window_weight;

              /* FFT transform */
              vector<complex<float>> fft_out = fft (frame);

              vector<int> up;
              vector<int> down;
              get_up_down (f, up, down);
              for (auto u : up)
                {
                  umag += log (abs (fft_out[u]));
                }
              for (auto d : down)
                {
                  dmag += log (abs (fft_out[d]));
                }
            }
        }
      if ((f % Params::frames_per_bit) == (Params::frames_per_bit - 1))
        {
          bit_vec.push_back ((umag > dmag) ? 1 : 0);
          umag = 0;
          dmag = 0;
        }
    }
  printf ("pattern %s\n", bit_vec_to_str (bit_vec).c_str());
  if (!orig_pattern.empty())
    {
      int bits = 0, bit_errors = 0;

      vector<int> orig_vec = bit_str_to_vec (orig_pattern);
      for (size_t i = 0; i < bit_vec.size(); i++)
        {
          bits++;
          if (bit_vec[i] != orig_vec[i % orig_vec.size()])
            bit_errors++;
        }
      printf ("bit_error_rate %.5f %%\n", double (100.0 * bit_errors) / bits);
    }
  return 0;
}

int
get_watermark_delta (const string& origfile, const string& infile, const string& orig_pattern)
{
  WavData orig_wav_data;
  if (!orig_wav_data.load (origfile))
    {
      fprintf (stderr, "audiowmark: error loading %s: %s\n", origfile.c_str(), orig_wav_data.error_blurb());
      return 1;
    }

  WavData wav_data;
  if (!wav_data.load (infile))
    {
      fprintf (stderr, "audiowmark: error loading %s: %s\n", infile.c_str(), wav_data.error_blurb());
      return 1;
    }

  vector<int> bit_vec;
  double error0 = 0;
  double error1 = 0;

  for (int f = 0; f < frame_count (wav_data); f++)
    {
      for (int ch = 0; ch < wav_data.n_channels(); ch++)
        {
          /* prescale original data (may want to do energy normalization or similar instead) */
          vector<float> orig_frame = get_frame (orig_wav_data, f, ch);
          for (auto& orig_value : orig_frame)
            orig_value *= Params::pre_scale;
          vector<float> frame = get_frame (wav_data, f, ch);

          if (frame.size() == Params::frame_size)
            {
              vector<float> f0 = watermark_frame (orig_frame, f, 0);
              vector<float> f1 = watermark_frame (orig_frame, f, 1);

              for (size_t i = 0; i < frame.size(); i++)
                {
                  const double delta0 = frame[i] - f0[i];
                  error0 += delta0 * delta0;

                  const double delta1 = frame[i] - f1[i];
                  error1 += delta1 * delta1;
                }
            }
        }
      if ((f % Params::frames_per_bit) == (Params::frames_per_bit - 1))
        {
          bit_vec.push_back ((error0 > error1) ? 1 : 0);
          error0 = 0;
          error1 = 0;
        }
    }
  printf ("pattern %s\n", bit_vec_to_str (bit_vec).c_str());
  if (!orig_pattern.empty())
    {
      int bits = 0, bit_errors = 0;

      vector<int> orig_vec = bit_str_to_vec (orig_pattern);
      for (size_t i = 0; i < bit_vec.size(); i++)
        {
          bits++;
          if (bit_vec[i] != orig_vec[i % orig_vec.size()])
            bit_errors++;
        }
      printf ("bit_error_rate %.5f %%\n", double (100.0 * bit_errors) / bits);
    }
  return 0;
}

int
gentest (const string& infile, const string& outfile)
{
  printf ("generating test sample from '%s' to '%s'\n", infile.c_str(), outfile.c_str());

  WavData wav_data;
  if (!wav_data.load (infile))
    {
      fprintf (stderr, "audiowmark: error loading %s: %s\n", infile.c_str(), wav_data.error_blurb());
      return 1;
    }
  const vector<float>& in_signal = wav_data.samples();
  vector<float> out_signal;

  /* 10 seconds of audio - starting at 30 seconds of the original track */
  const size_t offset = 30 * wav_data.n_channels() * int (wav_data.mix_freq());
  const size_t n_samples = 10 * wav_data.n_channels() * int (wav_data.mix_freq());
  if (in_signal.size() < (offset + n_samples))
    {
      fprintf (stderr, "audiowmark: input file %s too short\n", infile.c_str());
      return 1;
    }
  for (size_t i = 0; i < n_samples; i++)
    {
      out_signal.push_back (in_signal[i + offset]);
    }
  WavData out_wav_data (out_signal, wav_data.n_channels(), wav_data.mix_freq(), wav_data.bit_depth());
  if (!out_wav_data.save (outfile))
    {
      fprintf (stderr, "audiowmark: error saving %s: %s\n", outfile.c_str(), out_wav_data.error_blurb());
      return 1;
    }
  return 0;
}

int
scale (const string& infile, const string& outfile)
{
  WavData wav_data;
  if (!wav_data.load (infile))
    {
      fprintf (stderr, "audiowmark: error loading %s: %s\n", infile.c_str(), wav_data.error_blurb());
      return 1;
    }

  const vector<float>& in_signal = wav_data.samples();
  vector<float> out_signal;
  for (size_t i = 0; i < in_signal.size(); i++)
    out_signal.push_back (in_signal[i] * Params::pre_scale);

  WavData out_wav_data (out_signal, wav_data.n_channels(), wav_data.mix_freq(), wav_data.bit_depth());
  if (!out_wav_data.save (outfile))
    {
      fprintf (stderr, "audiowmark: error saving %s: %s\n", outfile.c_str(), out_wav_data.error_blurb());
      return 1;
    }
  return 0;
}

int
get_snr (const string& origfile, const string& wmfile)
{
  WavData orig_wav_data;
  if (!orig_wav_data.load (origfile))
    {
      fprintf (stderr, "audiowmark: error loading %s: %s\n", origfile.c_str(), orig_wav_data.error_blurb());
      return 1;
    }

  WavData wav_data;
  if (!wav_data.load (wmfile))
    {
      fprintf (stderr, "audiowmark: error loading %s: %s\n", wmfile.c_str(), wav_data.error_blurb());
      return 1;
    }
  const vector<float>& orig_samples = orig_wav_data.samples();
  const vector<float>& samples = wav_data.samples();

  if (samples.size() != orig_samples.size())
    {
      fprintf (stderr, "audiowmark: files have different length\n");
      return 1;
    }
  double delta_power = 0;
  double signal_power = 0;
  for (size_t i = 0; i < samples.size(); i++)
    {
      const double orig_scaled = orig_samples[i] * Params::pre_scale;
      const double delta       = samples[i] - orig_scaled;

      delta_power += delta * delta;
      signal_power += orig_scaled * orig_scaled;
    }
  delta_power /= samples.size();
  signal_power /= samples.size();

  printf ("snr_db %f\n", 10 * log10 (signal_power / delta_power));
  return 0;
}

int
main (int argc, char **argv)
{
  parse_options (&argc, &argv);

  string op = (argc >= 2) ? argv[1] : "";

  if (op == "add" && argc == 5)
    {
      return add_watermark (argv[2], argv[3], argv[4]);
    }
  else if (op == "get" && argc == 3)
    {
      return get_watermark (argv[2], /* no ber */ "");
    }
  else if (op == "cmp" && argc == 4)
    {
      return get_watermark (argv[2], argv[3]);
    }
  else if (op == "gentest" && argc == 4)
    {
      return gentest (argv[2], argv[3]);
    }
  else if (op == "snr" && argc == 4)
    {
      get_snr (argv[2], argv[3]);
    }
  else if (op == "scale" && argc == 4)
    {
      scale (argv[2], argv[3]);
    }
  else if (op == "get-delta" && argc == 4)
    {
      return get_watermark_delta (argv[2], argv[3], /* no ber */ "");
    }
  else if (op == "cmp-delta" && argc == 5)
    {
      return get_watermark_delta (argv[2], argv[3], argv[4]);
    }
  else
    {
      fprintf (stderr, "audiowmark: error parsing commandline args\n");
      return 1;
    }
}


code:
#/bin/bash

echo 512 $(ber-test.sh)
for quality in 256 196 128 96 64
do
  echo $quality $(ber-test.sh double-mp3 $quality)
done

repo_name: Tonypythony/audiowmark

license: unknown


code:
#/bin/bash

echo 512 $(ber-test.sh)
for quality in 256 196 128 96 64
do
  echo $quality $(ber-test.sh mp3 $quality)
done


code:
#/bin/bash

echo 512 $(ber-test.sh)
for quality in 256 196 128 96 64
do
  echo $quality $(ber-test.sh ogg $quality)
done


code:
# pseudo random pattern

PATTERN=4e1243bd22c66e76c2ba9eddc1f91394e57f9f83
TRANSFORM=$1

for i in test/T*
do
  echo $i
  audiowmark add "$i" t.wav $PATTERN >/dev/null
  if [ "x$TRANSFORM" == "xmp3" ]; then
    if [ "x$2" == "x" ]; then
      echo "need mp3 bitrate" >&2
      exit 1
    fi
    lame -b $2 t.wav t.mp3 --quiet
    rm t.wav
    ffmpeg -i t.mp3 t.wav -v quiet

    # some (low) mpeg quality settings use a lower sample rate
    if [ "x$(soxi -r t.wav)" != "x44100" ]; then
      sox t.wav tr.wav rate 44100
      mv tr.wav t.wav
    fi
  elif [ "x$TRANSFORM" == "xdouble-mp3" ]; then
    if [ "x$2" == "x" ]; then
      echo "need mp3 bitrate" >&2
      exit 1
    fi
    # first mp3 step (fixed bitrate)
    lame -b 128 t.wav t.mp3 --quiet
    rm t.wav
    ffmpeg -i t.mp3 t.wav -v quiet

    # second mp3 step
    lame -b $2 t.wav t.mp3 --quiet
    rm t.wav
    ffmpeg -i t.mp3 t.wav -v quiet

    # some (low) mpeg quality settings use a lower sample rate
    if [ "x$(soxi -r t.wav)" != "x44100" ]; then
      sox t.wav tr.wav rate 44100
      mv tr.wav t.wav
    fi
  elif [ "x$TRANSFORM" == "xogg" ]; then
    if [ "x$2" == "x" ]; then
      echo "need ogg bitrate" >&2
      exit 1
    fi
    oggenc -b $2 t.wav -o t.ogg --quiet
    oggdec t.ogg -o t.wav --quiet
  elif [ "x$TRANSFORM" == "x" ]; then
    :
  else
    echo "unknown transform $TRANSFORM" >&2
    exit 1
  fi
  audiowmark cmp t.wav $PATTERN
done | grep bit_error_rate | awk '{ er += $2; n++; if ($2 > max_er) max_er = $2;} END { print er / n, max_er; }'


code:
#include "fft.hh"

#include <fftw3.h>

using std::vector;
using std::complex;

float *
new_array_float (size_t N)
{
  const size_t N_2 = N + 2; /* extra space for r2c extra complex output */

  return (float *) fftwf_malloc (sizeof (float) * N_2);
}

void
free_array_float (float *f)
{
  fftwf_free (f);
}

void
fftar_float (size_t N, float *in, float *out)
{
  static fftwf_plan plan = nullptr; // FIXME: should be one plan per fft size

  if (!plan)
    {
      float *plan_in = new_array_float (N);
      float *plan_out = new_array_float (N);
      plan = fftwf_plan_dft_r2c_1d (N, plan_in, (fftwf_complex *) plan_out, FFTW_ESTIMATE | FFTW_PRESERVE_INPUT);

      // we add code for saving plans here, and use patient planning
    }
  fftwf_execute_dft_r2c (plan, in, (fftwf_complex *) out);
}

void
fftsr_float (size_t N, float *in, float *out)
{
  static fftwf_plan plan = nullptr; // FIXME: should be one plan per fft size

  if (!plan)
    {
      float *plan_in = new_array_float (N);
      float *plan_out = new_array_float (N);
      plan = fftwf_plan_dft_c2r_1d (N, (fftwf_complex *) plan_in, plan_out, FFTW_ESTIMATE | FFTW_PRESERVE_INPUT);

      // we add code for saving plans here, and use patient planning
    }
  fftwf_execute_dft_c2r (plan, (fftwf_complex *)in, out);
}

vector<complex<float>>
fft (const vector<float>& in)
{
  vector<complex<float>> out (in.size() / 2 + 1);

  /* ensure memory is SSE-aligned (or other vectorized stuff) */
  float *fft_in = new_array_float (in.size());
  float *fft_out = new_array_float (in.size());

  std::copy (in.begin(), in.end(), fft_in);
  fftar_float (in.size(), fft_in, fft_out);

  /* complex<float> vector and fft_out have the same layout in memory */
  std::copy (fft_out, fft_out + out.size() * 2, reinterpret_cast<float *> (&out[0]));

  free_array_float (fft_out);
  free_array_float (fft_in);

  return out;
}

vector<float>
ifft (const vector<complex<float>>& in)
{
  vector<float> out ((in.size() - 1) * 2);

  /* ensure memory is SSE-aligned (or other vectorized stuff) */
  float *ifft_in = new_array_float (out.size());
  float *ifft_out = new_array_float (out.size());

  /* complex<float> vector and fft_out have the same layout in memory */
  std::copy (in.begin(), in.end(), reinterpret_cast<complex<float> *> (ifft_in));
  fftsr_float (out.size(), ifft_in, ifft_out);

  std::copy (ifft_out, ifft_out + out.size(), &out[0]);

  free_array_float (ifft_out);
  free_array_float (ifft_in);

  return out;
}


code:
#ifndef AUDIOWMARK_FFT_HH
#define AUDIOWMARK_FFT_HH

#include <complex>
#include <vector>

std::vector<std::complex<float>> fft (const std::vector<float>& in);
std::vector<float>               ifft (const std::vector<std::complex<float>>& in);

#endif /* AUDIOWMARK_FFT_HH */


code:
#!/bin/bash

mkdir -p test

seq=1
cat test_list | while read f
do
  audiowmark gentest "$f" test/T$(printf "%02d__%s" $seq $(echo $f | sed 's, ,_,g;s,.*/,,g')).wav
  ((seq++))
done


code:
# pseudo random pattern

PATTERN=4e1243bd22c66e76c2ba9eddc1f91394e57f9f83
TRANSFORM=$1

peaq_print_scores()
{
  awk '
    BEGIN {
      odg = "*ERROR*";
      di  = "*ERROR*";
    }
    /Objective Difference Grade:/ {
      odg = $NF;
    }
    /Distortion Index:/ {
      di = $NF;
    }
    END {
      print odg, di
    }'
}

for i in test/T*
do
  audiowmark add "$i" t.wav $PATTERN >/dev/null
  audiowmark scale "$i" t_in.wav
  echo $i $(peaq t_in.wav t.wav | peaq_print_scores)
done | awk 'BEGIN {
    max=-10;
    min=10;
    avg=0;
    count=0;
  }
  {
    if ($2 > max)
      max = $2;
    if ($2 < min)
      min = $2;
    avg += $2;
    count++;
  }
  END {
    avg /= count
    printf ("%7.3f %7.3f %7.3f\n", avg, min, max);
  }'


code:
# pseudo random pattern

PATTERN=4e1243bd22c66e76c2ba9eddc1f91394e57f9f83

for i in test/T*
do
  audiowmark add $i t.wav $PATTERN >/dev/null
  echo $i $(audiowmark snr $i t.wav)
done | grep snr | awk '{s += $3; n++} END { print s/n; }'


code:
/home/stefan/files/music/artists/air/2009__love_2/09_sing_sang_sung.flac
/home/stefan/files/music/artists/jamie_cullum/the_pursuit/12_music_is_through.flac
/home/stefan/files/music/artists/gotye/making_mirrors/10 - Giving Me A Chance.flac
/home/stefan/files/music/artists/alice_cooper/the_definitive_alice_cooper/18_how_you_gonna_see_me_now.flac
/home/stefan/files/music/artists/mendelssohn/The Hebrides, Symphonies Nos.1, 4/02 - Symphony No. 1 C minor Op. 11 - Allegro di molto.flac
/home/stefan/files/music/artists/daft_punk/tron_legacy/15 - Solar Sailer.flac
/home/stefan/files/music/artists/depeche_mode/the_singles_81_to_85/09 - Love In Itself.flac
/home/stefan/files/music/artists/beethoven/Klavierkonzerte Nrr.3 & 4 (CD 2)/02 - Klavierkonzert Nr. 3 C-moll - 2. Largo.flac
/home/stefan/files/music/artists/joe_henderson/page_one/05 - Jinrikisha.flac
/home/stefan/files/music/artists/boehse_onkelz/gehasst_verdammt_vergoettert/15_fr_immer.flac
/home/stefan/files/music/artists/dj_bobo/because_of_you/03 - Because Of You (Twister Hard Club - Radio Edit).flac
/home/stefan/files/music/artists/mixed/the_world_of_trance_2/cd1/10 - Nature One - The Sense Of Live (Hurricanmix).flac
/home/stefan/files/music/artists/charles_mingus/Mingus Plays Piano/08 - Meditations for Moses.flac
/home/stefan/files/music/artists/lena/good_news/06 - Mama Told Me.flac
/home/stefan/files/music/artists/scriabin/Horowitz Plays Scriabin/06 - Scriabin - Prelude, Op.11, No.16.flac
/home/stefan/files/music/artists/paniq/beyond_good_and_evil/paniq - Beyond Good and Evil - 02 Tartaros (The Barren Acres of Open Source).flac
/home/stefan/files/music/artists/mixed/Katia Marielle Labeque - Rhapsody in Blue/05 - Strawinsky: Petrushka_Volksfest waehrend der Fastnacht.flac
/home/stefan/files/music/artists/rosenstolz/mondkuss_cd1/09_die_zigarette_danach.flac
/home/stefan/files/music/artists/tears_for_fears/the_collection/03 - Shout.flac
/home/stefan/files/music/artists/duke_ellington/Kings of Swing - Anthology/18 - The Hawk Talks.flac
/home/stefan/files/music/artists/ich__ich/gute_reise__cd_1/09_stein.flac
/home/stefan/files/music/artists/brahms/Double Concerto for Violin and Cello 1 Am Op102/05 - Brahms Double Concerto for Violin and Cello 2 Am Op102 Andante.flac
/home/stefan/files/music/artists/mixed/Katia Marielle Labeque - Rhapsody in Blue/01 - Gershwin: Rhapsody in Blue.flac
/home/stefan/files/music/artists/loreena_mckennitt/collection/04 - Loreena Mckennitt - Caravanserai.flac
/home/stefan/files/music/artists/mumford_and_sons/sigh_no_more/05 - White Blank Page.flac


code:
#include "wavdata.hh"

#include <math.h>
#include <sndfile.h>

using std::string;
using std::vector;

template<typename T>
inline const T&
bound (const T& min_value, const T& value, const T& max_value)
{
  return std::min (std::max (value, min_value), max_value);
}

WavData::WavData()
{
}

WavData::WavData (const vector<float>& samples, int n_channels, float mix_freq, int bit_depth)
{
  m_samples     = samples;
  m_n_channels  = n_channels;
  m_mix_freq    = mix_freq;
  m_bit_depth   = bit_depth;
}

bool
WavData::load (const string& filename)
{
  SF_INFO sfinfo = { 0, };

  SNDFILE *sndfile = sf_open (filename.c_str(), SFM_READ, &sfinfo);

  int error = sf_error (sndfile);
  if (error)
    {
      m_error_blurb = sf_strerror (sndfile);
      if (sndfile)
        sf_close (sndfile);

      return false;
    }

  vector<int> isamples (sfinfo.frames * sfinfo.channels);
  sf_count_t count = sf_readf_int (sndfile, &isamples[0], sfinfo.frames);

  error = sf_error (sndfile);
  if (error)
    {
      m_error_blurb = sf_strerror (sndfile);
      sf_close (sndfile);

      return false;
    }

  if (count != sfinfo.frames)
    {
      m_error_blurb = "reading sample data failed: short read";
      sf_close (sndfile);

      return false;
    }

  m_samples.resize (sfinfo.frames * sfinfo.channels);

  /* reading a wav file and saving it again with the libsndfile float API will
   * change some values due to normalization issues:
   *   http://www.mega-nerd.com/libsndfile/FAQ.html#Q010
   *
   * to avoid the problem, we use the int API and do the conversion beween int
   * and float manually - the important part is that the normalization factors
   * used during read and write are identical
   */
  const double norm = 1.0 / 0x80000000LL;
  for (size_t i = 0; i < m_samples.size(); i++)
    m_samples[i] = isamples[i] * norm;

  m_mix_freq    = sfinfo.samplerate;
  m_n_channels  = sfinfo.channels;

  switch (sfinfo.format & SF_FORMAT_SUBMASK)
    {
      case SF_FORMAT_PCM_U8:
      case SF_FORMAT_PCM_S8:
          m_bit_depth = 8;
          break;

      case SF_FORMAT_PCM_16:
          m_bit_depth = 16;
          break;

      case SF_FORMAT_PCM_24:
          m_bit_depth = 24;
          break;

      case SF_FORMAT_FLOAT:
      case SF_FORMAT_PCM_32:
          m_bit_depth = 32;
          break;

      case SF_FORMAT_DOUBLE:
          m_bit_depth = 64;
          break;

      default:
          m_bit_depth = 32; /* unknown */
    }

  error = sf_close (sndfile);
  if (error)
    {
      m_error_blurb = sf_error_number (error);
      return false;
    }
  return true;
}

bool
WavData::save (const string& filename)
{
  SF_INFO sfinfo = {0,};

  sfinfo.samplerate = lrint (m_mix_freq);
  sfinfo.channels   = m_n_channels;

  if (m_bit_depth > 16)
    sfinfo.format = SF_FORMAT_WAV | SF_FORMAT_PCM_24;
  else
    sfinfo.format = SF_FORMAT_WAV | SF_FORMAT_PCM_16;

  SNDFILE *sndfile = sf_open (filename.c_str(), SFM_WRITE, &sfinfo);
  int error = sf_error (sndfile);
  if (error)
    {
      m_error_blurb = sf_strerror (sndfile);
      if (sndfile)
        sf_close (sndfile);

      return false;
    }

  vector<int> isamples (m_samples.size());
  for (size_t i = 0; i < m_samples.size(); i++)
    {
      const double norm      =  0x80000000LL;
      const double min_value = -0x80000000LL;
      const double max_value =  0x7FFFFFFF;

      isamples[i] = lrint (bound<double> (min_value, m_samples[i] * norm, max_value));
    }

  sf_count_t frames = m_samples.size() / m_n_channels;
  sf_count_t count = sf_writef_int (sndfile, &isamples[0], frames);

  error = sf_error (sndfile);
  if (error)
    {
      m_error_blurb = sf_strerror (sndfile);
      sf_close (sndfile);

      return false;
    }

  if (count != frames)
    {
      m_error_blurb = "writing sample data failed: short write";
      sf_close (sndfile);

      return false;
    }

  error = sf_close (sndfile);
  if (error)
    {
      m_error_blurb = sf_error_number (error);
      return false;
    }
  return true;
}

float
WavData::mix_freq() const
{
  return m_mix_freq;
}

int
WavData::n_channels() const
{
  return m_n_channels;
}

int
WavData::bit_depth() const
{
  return m_bit_depth;
}

const vector<float>&
WavData::samples() const
{
  return m_samples;
}

size_t
WavData::n_values() const
{
  return m_samples.size();
}

const char *
WavData::error_blurb() const
{
  return m_error_blurb.c_str();
}


code:
#ifndef AUDIOWMARK_WAV_DATA_HH
#define AUDIOWMARK_WAV_DATA_HH

#include <string>
#include <vector>

class WavData
{
  std::vector<float> m_samples;
  float              m_mix_freq   = 0;
  int                m_n_channels = 0;
  int                m_bit_depth  = 0;
  std::string        m_error_blurb;

public:
  WavData();
  WavData (const std::vector<float>& samples, int n_channels, float mix_freq, int bit_depth);

  bool load (const std::string& filename);
  bool save (const std::string& filename);

  float                       mix_freq() const;
  int                         n_channels() const;
  size_t                      n_values() const;
  int                         bit_depth() const;
  const std::vector<float>&   samples() const;
  const char                 *error_blurb() const;
};

#endif /* AUDIOWMARK_WAV_DATA_HH */


code:
#*#
compiled

repo_name: Regrin/prospect

path: .gitignore


code:
# Проспект-А
Проспект-А - это симулятор компьютера который можно программировать во встроенном редакторе. Программа нужна для того, чтобы изучать устройство компьютера на низком уровне.

Вдохновлено программой Константина Полякова [Лампанель](https://kpolyakov.spb.ru/prog/lamp.htm).

Сейчас программа находится на раннем этапе разработки.

## Цели
- Сделать обучение ассемблеру лёгким и доступным.
- Облегчить эксперементы с компиляторами.

## Возможности
Когда проект будет реализован, он будет предоставлят следующии возможности:

- Выбор архитектуры процессора
- Написание и отладка кода на ассемблере и других языках.
- Подключение виртуальной переферии: панели лампочек, терминала, дисков, модема
- Простое расширение программы новыми языками и архитектурами.

## Разработка
Программа пишется на языке Common Lisp. Планируется создать мощные средства быстрой разработки новых архитектур и ассемблеров для них.

## Лицензия
Приложение распространяется по лицензии GNU GPL3 и выше.


code: (in-package :prospect-a)


code:
(defpackage :prospect-a
  (:use :cl )
  (:export))


code:
(asdf:defsystem :prospect-a
  :description ""
  :author "Filipp Druan druanf@bk.ru"
  :license "GNU GPL3"
  :version "0.0.0"
  :depends-on (alexandria
               anaphora)
  :components ((:file "package")))


code:
avatar/
files/
file/
local/
logs/
log/
run/
config.php
.htaccess
httpd.conf
dataobject.ini
*.bak
*.orig
*.rej
TODO.rym
config-*.php
good-config.php
*.mo
/vendor/

license: mit


code:
# GNU social - Log of Changes

## 2.0.0 - THIS. IS. GNU SOCIAL!!! [WIP]

Release name chosen after 300 by Frank Miller where the main protagonist Leonidas, King of Sparta, declines peace with the
Persians, after being disrespected, by shouting at the Persian Messenger "This is Sparta!" and kicking him into a large well
proceeded by the killing of the other Persian messengers.

### Major changes from previous release:

Load and Storage:
- New media handling system
- GS is now structurely divided in includes and public
- OEmbed upgraded to Embed plugin (Now we provide Open Graph information too)

General:
- Composer was integrated

Modules:
- Restored built-in plugins
- New modules system: core plugins and plugins physically separated
- Refactor of Plugin API to better illustrate the idea of modules
- Bug fixes of core modules logic

#### TODO before alpha:

Load and Storage:
- Upgrade STOMP queue
- Add Redis based caching and queues
- Review memcached based cache
- Port PEAR DB to PDO_DataObject
- Support PostgreSQL

Network:
- Port PEAR HTTP to Guzzle
- Port PEAR Mail to PHPSendMail
- Add OAuth2 support (deprecate OAuth1)
- Add shinny new Plugins management interface for sysadmins together with a new doc for devs

Federation:
- Add ActivityPub support
  - Fix audience targeting
  - Add Group Actor Type
- OstatusSub: Remote follow OS and AP profiles via OStatusSub
- ActorLists: Allow to create collections of Actors and to interact with them - supports both OS and AP
- The Free Network: Automagically migrate internal remote profiles between Free Network protocols (check Nodeinfo)
- Enable the search box to import remote notices and profiles

General:
- Fix failling unit tests
- Improve Cronish
  - Run session garbage collection
  - Cleanup Email Registration
- Refactoring of confirmation codes
- Refactoring of Exceptions

Modules:
- Document conversion of older plugins to the new GS 2
- Create installer for v2 plugins
- Introduce new metadata for plugins (category and thumb)
- Improve plugin management tool (add install form and better UI that makes use of new metadata)
- Add plugin management tool as a install step
- Allow to install remote plugins and suggest popular trusted ones

## v1.20.9release - The Invicta Crusade

Release name chosen after Porto city. Porto is one of the oldest cities in Europe and thanks to its fierce resistance
during two battles and sieges in history, it has earned the epithet of ‘Cidade Invicta’ (Invincible City). The dev team
behind this release studies in Porto, Portugal.

Dropped Support for PHP5.6.x. Minimum PHP version now is 7.0.0.

Major changes from previous release:

- Various patches on PEAR related components
- Various database related improvements
- Improved XMPP support
- Added Nodeinfo support
- Various i18n and l10n bug fixes
- Improvements on Internal Session Handler
- Improvements on OpenID support
- Improved Media handling and safer upload
- Redirect to previous page after login
- Initial work on full conversion to PHP7
- Initial work on a better documentation
- Allow login with email
- Various bug fixes

## v1.2.0beta4 - The good reign of PHP5

Dropped support for PHP5.4.

New this version

This is the development branch for the 1.2.x version of GNU social. All daring 1.1.x admins should upgrade to this version.

So far it includes the following changes:

- Backing up a user's account is more and more complete.
- Emojis 😸 (utf8mb4 support)

The last release, 1.1.3, gave us these improvements:

- XSS security fix (thanks Simon Waters, https://www.surevine.com/)
- Many improvements to ease adoption of the Qvitter front-end https://github.com/hannesmannerheim/qvitter
- Protocol adaptions for improved performance and stability

Upgrades from StatusNet 1.1.1 will also experience these improvements:

- Fixes for SQL injection errors in profile lists.
- Improved ActivityStreams JSON representation of activities and objects.
- Upgrade to the Twitter 1.1 API.
- More robust handling of errors in distribution.
- Fix error in OStatus subscription for remote groups.
- Fix error in XMPP distribution.
- Tracking of conversation URI metadata (more coherent convos)

## v1.1.3release - The Spanish Invasion

New this version

This is a security fix and bug fix release since 1.1.3-beta2. All 1.1.x sites should upgrade to this version.

So far it includes the following changes:

- XSS security fix (thanks Simon Waters, https://www.surevine.com/)
- Many improvements to ease adoption of the Qvitter front-end https://github.com/hannesmannerheim/qvitter
- Protocol adaptions for improved performance and stability
- Backing up a user's account now appears to work as it should

Upgrades from StatusNet 1.1.1 will also experience these improvements:

- Fixes for SQL injection errors in profile lists.
- Improved ActivityStreams JSON representation of activities and objects.
- Upgrade to the Twitter 1.1 API.
- More robust handling of errors in distribution.
- Fix error in OStatus subscription for remote groups.
- Fix error in XMPP distribution.
- Tracking of conversation URI metadata (more coherent convos)


code:
## Code of Conduct

### Our Pledge

In the interest of fostering an open and welcoming environment, we as
contributors and maintainers pledge to making participation in our project and
our community a harassment-free experience for everyone, regardless of age, body
size, disability, ethnicity, gender identity and expression, level of experience,
nationality, personal appearance, race, religion, or sexual identity and
orientation.

### Our Standards

Examples of behavior that contributes to creating a positive environment
include:

* Using welcoming and inclusive language
* Being respectful of differing viewpoints and experiences
* Gracefully accepting constructive criticism
* Focusing on what is best for the community
* Showing empathy towards other community members

Examples of unacceptable behavior by participants include:

* The use of sexualized language or imagery and unwelcome sexual attention or
advances
* Trolling, insulting/derogatory comments, and personal or political attacks
* Public or private harassment
* Publishing others' private information, such as a physical or electronic
  address, without explicit permission
* Other conduct which could reasonably be considered inappropriate in a
  professional setting

### Our Responsibilities

Project maintainers are responsible for clarifying the standards of acceptable
behavior and are expected to take appropriate and fair corrective action in
response to any instances of unacceptable behavior.

Project maintainers have the right and responsibility to remove, edit, or
reject comments, commits, code, wiki edits, issues, and other contributions
that are not aligned to this Code of Conduct, or to ban temporarily or
permanently any contributor for other behaviors that they deem inappropriate,
threatening, offensive, or harmful.

### Scope

This Code of Conduct applies both within project spaces and in public spaces
when an individual is representing the project or its community. Examples of
representing a project or community include using an official project e-mail
address, posting via an official social media account, or acting as an appointed
representative at an online or offline event. Representation of a project may be
further defined and clarified by project maintainers.

### Enforcement

Instances of abusive, harassing, or otherwise unacceptable behavior may be
reported by contacting the project team at mattl@gnu.org. All
complaints will be reviewed and investigated and will result in a response that
is deemed necessary and appropriate to the circumstances. The project team is
obligated to maintain confidentiality with regard to the reporter of an incident.
Further details of specific enforcement policies may be posted separately.

Project maintainers who do not follow or enforce the Code of Conduct in good
faith may face temporary or permanent repercussions as determined by other
members of the project's leadership.

### Attribution

This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4,
available at [http://contributor-covenant.org/version/1/4][version]

[homepage]: http://contributor-covenant.org
[version]: http://contributor-covenant.org/version/1/4/

## The Code of Conflict

GNU social has a high submission standard and we want to keep quality code in the
codebase and bad code out of it. As such your code will be closely scrutinized,
and you might take this criticism personally. Please understand that this is
meant to keep the standards of the codebase up, and isn't meant personally. All
the same, this isn't an excuse for poor behaviour, and a reviewer shouldn't be
misbehaving towards submitters.

If however, anyone feels personally abused, threatened, or otherwise
uncomfortable due to this process, that is not acceptable.  If so, please
contact the project team at mattl@gnu.org, and they will work to resolve the issue
to the best of their ability.

As a reviewer of code, please strive to keep things civil and focused on the
technical issues involved.  We are all humans, and frustrations can be high on
both sides of the process.  Try to keep in mind the immortal words of Bill and
Ted, "Be excellent to each other."


code: GNU social's contributing resources and instructions are made available at [DOCUMENTATION/DEVELOPERS](https://notabug.org/diogo/gnu-social/src/nightly/DOCUMENTATION/DEVELOPERS).


code:
Credits for GNU social
======================
The following is an incomplete list of developers
who've worked on GNU social, or its predecessors
StatusNet and Free Social. Apologies for any
oversight; please let mattl@gnu.org know if
anyone's been overlooked in error.

Current team
------------
* Matt Lee
* Mikael Nordfeldth
* Diogo Cordeiro
* Bruno Casteleiro
* Miguel Dantas
* Alexei Sorokin

Additional Contributors
-----------------------
* Ciaran Gultnieks
* Michael Landers
* Ori Avtalion
* Garret Buell
* Mike Cochrane
* Matthew Gregg
* Sean Murphy
* Leslie Michael Orchard
* Eric Helgeson
* Ken Sedgwick
* Brian Hendrickson
* Tobias Diekershoff
* Dan Moore
* Fil
* Jeff Mitchell
* Brenda Wallace
* Jeffery To
* Federico Marani
* mEDI
* Brett Taylor
* Brigitte Schuster
* Craig Andrews
* Donald Robertson
* Deb Nicholson
* Ian Denhart
* Steven DuBois
* Blaine Cook
* Henry Story
* Melvin Carvalho
* chimo
* Akio
* Maiyannah Bishop
* Bob Mottram
* David Yip
* Neil E Hodges
* Moonman
* Normandy
* Verius
* Alexei Sorokin
* Daniel Supernault

Credits for StatusNet
--------------
Leads
* Evan Prodromou
* Zach Copley

Team
* Earle Martin
* Marie-Claude Doyon
* Sarven Capadisli
* Robin Millette
* Brion Vibber
* James Walker
* Samantha Doherty
* Florian Biree
* Erik Stambaugh
* 'drry'
* Gina Haeussge
* Tryggvi Björgvinsson
* Adrian Lang
* Ori Avtalion
* Meitar Moscovitz
* Ken Sheppardson
* Simon Waters, Surevine
* Joshua Judson Rosen (rozzin)

Translators
-----------
* Siebrand Mazeland
* Tiago 'gouki' Faria
* TranslateWiki.net

A special thanks to the thousands of people who
have tried out GNU social, told their friends, and
built the fediverse network to what it is today.

License help from
-----------------
* Bradley M. Kuhn


code:
<?php
// This file is part of GNU social - https://www.gnu.org/software/social
//
// GNU social is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// GNU social 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 Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with GNU social.  If not, see <http://www.gnu.org/licenses/>.

/**
 * Description of this file.
 *
 * @package   samples
 * @author    Diogo Cordeiro <diogo@fc.up.pt>
 * @copyright 2019 Free Software Foundation, Inc http://www.fsf.org
 * @license   https://www.gnu.org/licenses/agpl.html GNU AGPL v3 or later
 */

namespace samples;

defined('GNUSOCIAL') || die();

require_once __DIR__ . DIRECTORY_SEPARATOR . 'SampleHandler.php';

/**
 * Description of this class.
 *
 * @copyright 2019 Free Software Foundation, Inc http://www.fsf.org
 * @license   https://www.gnu.org/licenses/agpl.html GNU AGPL v3 or later
 */
class MySampleClass
{
    /**
     * Constructor for the sample class.
     *
     * @param string $dummy_word just because.
     * @param int $result another just because.
     */
    public function __construct(string $dummy_word = '', ?int $result = null)
    {
        global $demo;
        $this->niceWorld();
    }

    /**
     * How cool is this function.
     *
     * @return string
     */
    public function niceWorld(): string
    {
        return 'hello, world.';
    }
}


code:
GNU social Coding Style
===========================

Please comply with [PSR-2](https://www.php-fig.org/psr/psr-2/) and the following standard when working on GNU social
if you want your patches accepted and modules included in supported releases.

If you see code which doesn't comply with the below, please fix it :)

Strings
-------------------------------------------------------------------------------
Use `'` instead of `"` for strings, where substitutions aren't required.
This is a performance issue, and prevents a lot of inconsistent coding styles.
When using substitutions, use curly braces around your variables - like so:

    $var = "my_var: {$my_var}";

Comments and Documentation
-------------------------------------------------------------------------------
Comments go on the line ABOVE the code, NOT to the right of the code, unless it is very short.
All functions and methods are to be documented using PhpDocumentor - https://docs.phpdoc.org/guides/

File Headers
-------------------------------------------------------------------------------
File headers follow a consistent format, as such:

    // This file is part of GNU social - https://www.gnu.org/software/social
    //
    // GNU social is free software: you can redistribute it and/or modify
    // it under the terms of the GNU Affero General Public License as published by
    // the Free Software Foundation, either version 3 of the License, or
    // (at your option) any later version.
    //
    // GNU social 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 Affero General Public License for more details.
    //
    // You should have received a copy of the GNU Affero General Public License
    // along with GNU social.  If not, see <http://www.gnu.org/licenses/>.

    /**
     * Description of this file.
     *
     * @package   samples
     * @author    Diogo Cordeiro <diogo@fc.up.pt>
     * @copyright 2019 Free Software Foundation, Inc http://www.fsf.org
     * @license   https://www.gnu.org/licenses/agpl.html GNU AGPL v3 or later
     */

Please use it.

A few notes:

*  The description of the file doesn't have to be exhaustive.  Rather it's
   meant to be a short summary of what's in this file and what it does.  Try
   to keep it to 1-5 lines.  You can get more in-depth when documenting
   individual functions!

*  You'll probably see files with multiple authors, this is by
   design - many people contributed to GNU social or its forebears!  If you
   are modifying an existing file, APPEND your own author line, and update
   the copyright year if needed. Do not replace existing ones.

You may find `boilerplate.php` useful when creating a new file from scratch.

Paragraph spacing
-------------------------------------------------------------------------------
Where-ever possible, try to keep the lines to 80 characters.  Don't
sacrifice readability for it though - if it makes more sense to have it in
one longer line, and it's more easily read that way, that's fine.

With assignments, avoid breaking them down into multiple lines unless
neccesary, except for enumerations and arrays.

'If' statements format
-------------------------------------------------------------------------------
Use switch statements where many else if's are going to be used. Switch/case is faster

    if ($var == 'example') {
        echo 'This is only an example';
    } else {
        echo 'This is not a test.  This is the real thing';
    }

Do NOT make if statements like this:

    if ($var == 'example'){ echo 'An example'; }

    OR this

    if($var = 'example')
            echo "An {$var}";

Associative arrays
-------------------------------------------------------------------------------
Always use `[]` instead of `array()`. Associative arrays must be written in the
following manner:

    $array = [
        'var' => 'value',
        'var2' => 'value2'
    ];

Note that spaces are preferred around the '=>'.

A note about shorthands
-------------------------------------------------------------------------------
Some short hands are evil:

- Use the long format for `<?php`.  Do NOT use `<?`.
- Use the long format for `<?php` echo. Do NOT use `<?=`.

Naming conventions
-------------------------------------------------------------------------------
Respect PSR2 first.

- Classes use PascalCase (e.g. MyClass).
- Functions/Methods use camelCase (e.g. myFunction).
- Variables use snake_case (e.g. my_variable).

A note on variable names, etc. It must be possible to understand what is meant
without neccesarialy seeing it in context, because the code that calls something
might not always make it clear.

So if you have something like:

    $notice->post($contents);

Well I can easily tell what you're doing there because the names are straight-
forward and clear.

Something like this:

    foo->bar();

Is much less clear.

Also, whereever possible, avoid ambiguous terms.  For example, don't use text
as a term for a variable.  Call back to "contents" above.

Comparisons
-------------------------------------------------------------------------------
Always use symbol based comparison operators (&&, ||) instead of text based
operators (AND, OR) as they are evaluated in different orders and at different
speeds.  This is will prevent any confusion or strange results.

Use English
-------------------------------------------------------------------------------
All variables, classes, methods, functions and comments must be in English.
Bad english is easier to work with than having to babelfish code to work out
how it works.

Encoding
-------------------------------------------------------------------------------
Files should be in UTF-8 encoding with UNIX line endings.

No ending tag
-------------------------------------------------------------------------------
Files should not end with an ending php tag "?>".  Any whitespace after the
closing tag is sent to the browser and cause errors, so don't include them.

Nesting Functions
-------------------------------------------------------------------------------
Avoid, if at all possible.  When not possible, document the living daylights
out of why you're nesting it.  It's not always avoidable, but PHP 5 has a lot
of obscure problems that come up with using nested functions.

If you must use a nested function, be sure to have robust error-handling.
This is a must and submissions including nested functions that do not have
robust error handling will be rejected and you'll be asked to add it.

Scoping
-------------------------------------------------------------------------------
Properly enforcing scope of functions is something many PHP programmers don't
do, but should.

In general:
*  Variables unique to a class should be protected and use interfacing to
   change them.  This allows for input validation and making sure we don't have
   injection, especially when something's exposed to the API, that any program
   can use, and not all of them are going to be be safe and trusted.

*  Variables not unique to a class should be validated prior to every call,
   which is why it's generally not a good idea to re-use stuff across classes
   unless there's significant performance gains to doing so.

*  Classes should protect functions that they do not want overriden, but they
   should avoid protecting the constructor and destructor and related helper
   functions as this prevents proper inheritance.

Typecasting
-------------------------------------------------------------------------------
PHP is a soft-typed language and it falls to us developers to make sure that
we are using the proper inputs.  Where ever possible use explicit type casting.
Where it in't, you're going to have to make sure that you check all your
inputs before you pass them.

All outputs should be cast as an explicit PHP type.

Not properly typecasting is a shooting offence.  Soft types let programmers
get away with a lot of lazy code, but lazy code is buggy code, and frankly, I
don't want it in GNU social if it's going to be buggy.

Consistent exception handling
-------------------------------------------------------------------------------
Consistency is key to good code to begin with, but it is especially important
to be consistent with how we handle errors.  GNU social has a variety of built-
in exception classes.  Use them, wherever it's possible and appropriate, and
they will do the heavy lifting for you.

Additionally, ensure you clean up any and all records and variables that need
cleanup in a function using try { } finally { } even if you do not plan on
catching exceptions (why wouldn't you, though?  That's silly.)

If you do not call an exception handler, you must, at a minimum, record errors
to the log using common_log(level, message)

Ensure all possible control flows of a function have exception handling and
cleanup, where appropriate.  Don't leave endpoints with unhandled exceptions.
Try not to leave something in an error state if it's avoidable.

Return values
-------------------------------------------------------------------------------
All functions must return a value.  Every single one.  This is not optional.

If you are simply making a procedure call, for example as part of a helper
function, then return boolean TRUE on success, and the exception on failure.

When returning the exception, return the whole nine yards, which is to say the
actual PHP exception object, not just an error message.

All return values not the above should be type cast, and you should sanitize
anything returned to ensure it fits into the cast.  You might technically make
an integer a string, for instance, but you should be making sure that integer
SHOULD be a string, if you're returning it, and that it is a valid return
value.

A vast majority of programming errors come down to not checking your inputs
and outputs properly, so please try to do so as best and thoroughly as you can.

Layout and Location of files
-------------------------------------------------------------------------------
`/actions/` contains files that determine what happens when something "happens":
for instance, when someone favourites or repeats a notice.  Code that is
related to a "happening" should go here.

`/classes/` contains abstract definitions of certain "things" in the codebase
such as a user or notice.  If you're making a new "thing", it goes here.

`/lib/` is basically the back-end.  Actions will call something in here to get
stuff done usually, which in turn will probably manipulate information stored
in one or more records represented by a class.

`/extlib/` is where external libraries are located.  If you include a new
external library, it goes here.

`/plugins/` This is a great way to modularize your own new features.  If you want
to create new core features for GNU social, it is probably best to create a
module unless you absolutely must override or modify the core behaviours.


code:
Submission Checklist
================================================================================
This document serves as a handy checklist for submitted merges and patches to
the GNU social project. Following it isn't a gaurantee a patch will be accepted,
but it will help you avoid common problems.

1. Ensure all code control paths in all functions return a value.

2. Ensure all exceptions are trapped in an exception class, or minimally,
   written to the log with common_log

3. Ensure the coding format standards are adhered to (see coding_standards.md)

4. Ensure that any new class that deals in public data has a corresponding new
   API endpoint.

5. Ensure that all new API endpoints sanitize inputs and outputs properly.

6. Ensure that your version of the code works with PHP 7 on a standard
   LAMP and LEMP stack (Linux+Apache+MariaDB+PHP and Linux+nginx+MariaDB+PHP)

7. If implementing new database functions, ensure they work with MariaDB
   and postgreSQL.

8. Ensure all data that federates does so properly and has mechanisms to
   catch and accomodate for federation transmission failure.

9. Ensure that nothing is left in an error state when it is avoidable.

10. Ensure that all code submitted is properly documented.

11. Ensure that there are no PHP Strict Standards or Parse errors in the code.