#include <iostream>
#include <fstream>
#include <string>
#include <cstdlib>
#include <vector>

using namespace std;

/*
   Program ten przetwarza 16-bitowe nieskompresowane monofoniczne pliki .WAV.
*/

/**
   Przetwarza próbki dźwięku. Zmodyfikuj tę funkcję, by zmienić
   sposób przetwarzania dźwięku.
   @param samples próbki dźwiękowe w pliku audio
   @param size liczba próbek
   @param sample_rate liczba próbek na sekundę
*/
void process(int samples[], int size, int sample_rate)
{
   // Tutaj sprawiamy, by dźwięk był trzy razy głośniejszy.
   // W tym przypadku nie potrzebujemy częstotliwości próbkowania.

   for (int i = 0; i < size; i++)
   {
      samples[i] = 3 * samples[i];
   }
}

// -------------------------------------------------------------------

/*
  Poniższy kod przetwarza plik w formacie WAV.
  Programu tego można użyć do manipulacji plikami dźwiękowymi bez
  znajomości ani zrozumienia poniższego kodu.

  W kodzie tym wykorzystano możliwości języka C++, które zostały przedstawione w rozdziałach 7. i 8.,
  oraz wewnętrzne mechanizmy formatu WAV.
  (https://ccrma.stanford.edu/courses/422/projects/WaveFormat/)
*/

/**
   Pobiera ze strumienia binarnego 4-bajtową liczbę całkowitą bez znaku.
   @param stream strumień
   @return liczba całkowita
*/
int get_unsigned_int4(fstream& stream)
{
   int result = 0;
   int base = 1;
   for (int i = 0; i < 4; i++)
   {
      result = result + stream.get() * base;
      base = base * 256;
   }
   return result;
}

/**
   Pobiera ze strumienia binarnego 2-bajtową liczbę całkowitą bez znaku.
   @param stream strumień
   @return liczba całkowita
*/
int get_unsigned_int2(fstream& stream)
{
   int lo = stream.get();
   int hi = stream.get();
   return lo + 256 * hi;
}

/**
   Pobiera ze strumienia binarnego 2-bajtową liczbę całkowitą ze znakiem.
   @param stream strumień
   @return liczba całkowita
*/
int get_signed_int2(fstream& stream)
{
   int lo = stream.get();
   int hi = stream.get();
   int result = lo + 256 * hi;
   if (result >= 32768) { result = result - 65536; }
   return result;
}

/**
   Umieszcza w strumieniu binarnym 2-bajtową liczbę całkowitą ze znakiem.
   @param stream strumień
   @param value umieszczana liczba całkowita
*/
void put_signed_int2(fstream& stream, int value)
{
   if (value < 0) { value = value + 65536; }
   stream.put(value % 256);
   stream.put(value / 256);
}

int main()
{
   cout << "Proszę wprowadzić nazwę pliku: ";
   string filename;
   cin >> filename;

   fstream stream;

   // Otwiera plik jako binarny.
   stream.open(filename.c_str(), ios::in | ios::out | ios::binary);

   // Sprawdza, czy można obsłużyć taki plik.
   stream.seekg(20);
   int format_type = get_unsigned_int2(stream);
   if (format_type != 1)
   {
      cout << "Plik dźwiękowy nie jest nieskompresowany." << endl;
      return 1;
   }
   int num_channels = get_unsigned_int2(stream);
   if (num_channels != 1)
   {
      cout << "Plik dźwiękowy nie jest monofoniczny." << endl;
      return 1;
   }

   int sample_rate = get_unsigned_int2(stream);

   stream.seekg(34);
   int bits_per_sample = get_unsigned_int2(stream);
   if (bits_per_sample != 16)
   {
      cout << "Plik dźwiękowy nie jest 16-bitowy." << endl;
      return 1;
   }

   // Odczytaj rozmiar danych i przydziel na nie tablicę
   stream.seekg(40);
   int data_size = get_unsigned_int4(stream) / 2; // 2 bajty na element danych
   int* samples = new int[data_size];

   // Odczytaj dane dźwięku.
   for (int i = 0; i < data_size; i++) 
   {
      samples[i] = get_signed_int2(stream);
   }

   // Przetwarzaj dane dźwiękowe.
   process(samples, data_size, sample_rate);

   // Zapisz dane dźwiękowe.
   stream.seekp(44);
   for (int i = 0; i < data_size; i++)
   {
      put_signed_int2(stream, samples[i]);
   }

   stream.close();

   delete[] samples;
     
   return 0;
}

