main.cpp 8.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279
  1. #include <cstdio>
  2. #include <cerrno>
  3. #include <numeric>
  4. #include <complex>
  5. #include "simple/io.h"
  6. #include "simple/compress.hpp"
  7. #include "simple/support.hpp"
  8. #include "simple/geom.hpp"
  9. using namespace simple;
  10. using namespace std::literals;
  11. const auto filetype = io::as_byte_view("SMOLENFILE");
  12. const auto audio_filetype = io::as_byte_view("RAWAUDIOFILE");
  13. const auto dct_audio_filetype = io::as_byte_view("DCTAUDIOFILE");
  14. struct audio_params_t
  15. {
  16. bool big_endian = false; // 1 bit
  17. std::uint8_t sample_type = 0; // 3 bits
  18. std::uint8_t channels = 2; // 4 bits
  19. std::uint32_t sample_rate = 48000; // 24 bits
  20. };
  21. const std::array sample_type_names {
  22. "int16"s,
  23. "int8"s,
  24. "uint16"s,
  25. "uint8"s,
  26. "int32"s,
  27. "uint32"s,
  28. "float"s,
  29. };
  30. using sample_types = support::meta::list<
  31. std::int16_t,
  32. std::int8_t,
  33. std::uint16_t,
  34. std::uint8_t,
  35. std::int32_t,
  36. std::uint32_t,
  37. float
  38. >;
  39. template <typename F>
  40. void with_sample_range(audio_params_t, io::byte_range bytes, F f)
  41. {
  42. // transform sample_types wrap them in geom::vector<T, params.channels>
  43. // pick the sample_types[params.sample_type]
  44. using sample_iterator = support::byte_iterator<geom::vector<std::int16_t, 2>>;
  45. f(support::range{sample_iterator{bytes.begin()}, sample_iterator{bytes.end()}});
  46. };
  47. int main(int argc, char const* argv[]) try
  48. {
  49. if(argc < 2)
  50. {
  51. std::fputs("argumentzzz \n", stderr);
  52. return 0;
  53. }
  54. std::optional<audio_params_t> audio_params;
  55. if(argc > 2)
  56. {
  57. // TODO
  58. audio_params = audio_params_t{};
  59. }
  60. std::vector<std::byte> data;
  61. {
  62. auto in_file = std::get<0>( io::open<io::mode::read>(argv[1]) );
  63. std::array<std::byte, 4096> buffer;
  64. support::copy(io::read_iterator(in_file, io::as_byte_range(buffer)), io::read_iterator(), std::back_inserter(data));
  65. }
  66. std::vector<std::byte> out_data;
  67. std::string out_filename = argv[1];
  68. const auto filetypematch = support::mismatch(data, filetype);
  69. if(filetypematch.second == filetype.end())
  70. {
  71. auto& decoded = out_data;
  72. // un-huffman
  73. decltype(compress::huffman_code(data.begin(), data.end())) code{};
  74. std::byte code_length{};
  75. auto i = compress::read_bits(filetypematch.first,code_length);
  76. while(code_length != std::byte{}) // TODO: check if i reaches end
  77. {
  78. decltype(code)::key_type key{};
  79. decltype(code)::value_type value{0, static_cast<std::size_t>(code_length)};
  80. i = compress::read_bits(i,value);
  81. i = compress::read_bits(i,key);
  82. code[key] = value;
  83. i = compress::read_bits(i,code_length);
  84. }
  85. std::vector<std::byte>::size_type decoded_size = 0;
  86. auto header_end = compress::read_bits(i, decoded_size);
  87. decoded.resize(decoded_size);
  88. compress::huffman_decode(code, header_end, decoded.begin(), decoded.end());
  89. // un-lz77
  90. std::swap(decoded, data);
  91. decoded_size = 0;
  92. header_end = compress::read_bits(data.begin(), decoded_size);
  93. decoded.resize(decoded_size);
  94. compress::lz77_decode(header_end, decoded.begin(), decoded.end());
  95. const auto audio_filetypematch = support::mismatch(decoded, dct_audio_filetype);
  96. if(audio_filetypematch.second == dct_audio_filetype.end())
  97. {
  98. // TODO: set audio_params from decoded
  99. }
  100. if(audio_params)
  101. {
  102. // un-dct
  103. using sample_type = geom::vector<std::int16_t,2>;
  104. using sample_iterator = support::byte_iterator<sample_type>;
  105. constexpr std::size_t chunk_size = (1 << 11) + 1;
  106. constexpr std::size_t dft_size = (chunk_size - 1) * 2;
  107. const std::size_t chunk_count = decoded.size() / (chunk_size * sizeof(sample_type));
  108. std::vector<geom::vector<float,2>> frequencies;
  109. // std::vector<geom::vector<std::complex<float>,2>> frequencies;
  110. frequencies.resize(dft_size);
  111. std::vector<geom::vector<std::complex<float>,2>> samples;
  112. samples.resize(dft_size);
  113. for(sample_iterator i{decoded.data()}; i != sample_iterator{decoded.data()} + chunk_count * chunk_size; i += chunk_size)
  114. {
  115. std::transform(i, i + chunk_size, frequencies.begin(),
  116. [](sample_type x) { return geom::vector<float,2>(x) / std::numeric_limits<std::int16_t>::max(); });
  117. std::copy(frequencies.rbegin()+chunk_size-1, frequencies.rend()-1, frequencies.begin() + chunk_size);
  118. const float tau = 2*std::acos(-1);
  119. compress::fft(std::multiplies<>{}, std::polar(1.f,tau/dft_size), frequencies, samples);
  120. std::transform(samples.begin(), samples.begin() + chunk_size, i, [](auto x)
  121. // TODO: clamp instead of assert?
  122. { return sample_type{x.transformed([](auto cx) { assert(std::abs(cx.real()) <= 1); return cx.real(); }) * std::numeric_limits<std::int16_t>::max()}; });
  123. }
  124. // TODO: add raw audio filetype header, if started with dct audio filetype just need to change the first 3 bytes
  125. }
  126. auto dot = support::find(support::make_range(out_filename).reverse(), '.');
  127. auto expected_ext = std::string_view{"smol"};
  128. if(dot == out_filename.rend() || not std::equal(dot.base(), out_filename.end(), expected_ext.begin(), expected_ext.end()))
  129. {
  130. std::fputs("where .smol? make .larg :/ \n", stderr);
  131. out_filename += ".larg";
  132. }
  133. else
  134. out_filename.erase(std::prev(dot.base()), out_filename.end());
  135. if(out_filename == "")
  136. {
  137. std::fputs("is this a jape >:( \n", stderr);
  138. out_filename = "harharharharharharharharharharharharhar.larg";
  139. }
  140. }
  141. else
  142. {
  143. const auto audio_filetypematch = support::mismatch(data, audio_filetype);
  144. if(audio_filetypematch.second == audio_filetype.end())
  145. {
  146. // TODO: set audio_params from data
  147. }
  148. if(audio_params)
  149. {
  150. // dct
  151. // TODO: actual audio params
  152. // FIXME: handle endianness
  153. using sample_type = geom::vector<std::int16_t,2>;
  154. using sample_iterator = support::byte_iterator<sample_type>;
  155. constexpr std::size_t chunk_size = (1 << 11) + 1;
  156. constexpr std::size_t dft_size = (chunk_size - 1) * 2;
  157. const std::size_t chunk_count = data.size() / (chunk_size * sizeof(sample_type));
  158. std::vector<geom::vector<float,2>> normalized;
  159. normalized.resize(dft_size);
  160. std::vector<geom::vector<std::complex<float>,2>> frequencies;
  161. frequencies.resize(dft_size);
  162. for(sample_iterator i{data.data()}; i != sample_iterator{data.data()} + chunk_count * chunk_size; i += chunk_size)
  163. {
  164. std::transform(i, i + chunk_size, normalized.begin(),
  165. [](sample_type x) { return geom::vector<float,2>(x) / std::numeric_limits<std::int16_t>::max(); });
  166. std::copy(normalized.rbegin()+chunk_size-1, normalized.rend()-1, normalized.begin() + chunk_size);
  167. const float tau = 2*std::acos(-1);
  168. compress::fft(std::multiplies<>{}, std::polar(1.f,tau/dft_size), normalized, frequencies);
  169. std::transform(frequencies.begin(), frequencies.begin() + chunk_size, i, [](auto x)
  170. // TODO: clamp instead of assert?
  171. { return sample_type{x.transformed([](auto x) { auto r = x.real() / dft_size; assert(std::abs(r) <= 1); return r; }) * std::numeric_limits<std::int16_t>::max()}; });
  172. // TODO: quantization paramter
  173. // { return sample_type{x.transformed([&dft_size](auto cx) { auto normalized = cx.real() / dft_size; return normalized < 0.01f ? 0.f : normalized; }) * std::numeric_limits<std::uint16_t>::max()}; });
  174. // TODO: paramter to discard higher frequencies
  175. // std::fill(i + chunk_size/2, i + chunk_size, sample_type{});
  176. }
  177. // TODO: add dct audio filetype header, if started with raw audio filetype just need to change the first 3 bytes
  178. }
  179. auto& encoded = out_data;
  180. encoded.reserve(data.size());
  181. // lz77
  182. auto out = compress::out_bits(support::offset_expander(encoded));
  183. *out = data.size();
  184. compress::lz77_encode(data.begin(), data.end(), std::move(out));
  185. out_filename += ".smol"s;
  186. if(encoded.size() > data.size())
  187. std::fputs("largenz :( \n", stderr);
  188. // huffman
  189. std::swap(encoded, data);
  190. encoded.resize(0);
  191. out = compress::out_bits(support::offset_expander(encoded));
  192. auto code = compress::huffman_code(data.begin(), data.end());
  193. code.for_each([&](auto kv)
  194. {
  195. auto code_bit_count = compress::bit_count(kv.second);
  196. if(code_bit_count != 0)
  197. {
  198. assert( code_bit_count < (1ull << compress::bit_count(std::byte{})) );
  199. *out = static_cast<std::byte>(code_bit_count);
  200. *out = kv.second;
  201. *out = kv.first;
  202. }
  203. });
  204. *out = static_cast<std::byte>(0);
  205. *out = data.size();
  206. compress::huffman_encode(code, data.begin(), data.end(), std::move(out));
  207. if(encoded.size() > data.size())
  208. std::fputs("huffpuffs :( \n", stderr);
  209. }
  210. auto out_file = std::get<0>( io::open<io::mode::write, io::mode::create, io::mode::truncate>(out_filename) );
  211. auto out_view = io::as_byte_view(out_data);
  212. if(filetypematch.second != filetype.end())
  213. if(std::get<0>(write(out_file, filetype)) != filetype.end()) throw std::runtime_error("Failed to write filetype to " + out_filename);
  214. if(std::get<0>(write(out_file, out_view)) != out_view.end()) throw std::runtime_error("Failed to write data to " + out_filename);
  215. return 0;
  216. }
  217. catch(const std::exception& e)
  218. {
  219. std::fputs(";( \n", stderr);
  220. if(errno)
  221. std::perror("");
  222. std::fputs(e.what(), stderr);
  223. std::fputs("\n", stderr);
  224. // throw;
  225. }