Guest User

Untitled

a guest
Apr 30th, 2025
177
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 16.01 KB | None | 0 0
  1. #include <iostream>
  2. #include <vector>
  3. #include <cmath>
  4. #include <fstream>
  5. #include <algorithm>
  6. #include <random> // For random number generation
  7. #include <cstdint> // For fixed-size integer types
  8.  
  9. const int NUM_FRAMES = 256;
  10. const int SAMPLES_PER_FRAME = 2048;
  11. const float SAMPLE_RATE = 44100.0f;
  12. const double TWO_PI = 6.28318530718;
  13.  
  14. // --- LFO Types ---
  15. enum class LFOType {
  16. SINE,
  17. COSINE,
  18. SAW_UP,
  19. SAW_DOWN,
  20. TRIANGLE, // Added triangle wave
  21. SQUARE, // Added square wave
  22. NUM_LFO_TYPES // Keep this at the end to easily get the count
  23. };
  24.  
  25. // --- LFO Function ---
  26. float lfo(float phase, LFOType type) {
  27. switch (type) {
  28. case LFOType::SINE:
  29. return std::sin(phase);
  30. case LFOType::COSINE:
  31. return std::cos(phase);
  32. case LFOType::SAW_UP:
  33. return fmod(phase, TWO_PI) / TWO_PI;
  34. case LFOType::SAW_DOWN:
  35. return 1.0f - (fmod(phase, TWO_PI) / TWO_PI);
  36. case LFOType::TRIANGLE: {
  37. float value = (2.0f * fmod(phase, TWO_PI) / TWO_PI) - 1.0f;
  38. return (value > 0) ? 1.0f - value : value + 1.0f; // Convert to -1 to 1 range
  39. }
  40. case LFOType::SQUARE:
  41. return (fmod(phase, TWO_PI) < M_PI) ? 1.0f : -1.0f; // Simple square wave
  42.  
  43. default:
  44. return 0.0f; // Should never happen, but good practice
  45. }
  46. }
  47.  
  48.  
  49.  
  50. // --- FM Operator Function ---
  51. float fmOperator(float time, float frequency, float modulationIndex, float modulator, float phaseOffset = 0.0f) {
  52. return std::sin(TWO_PI * frequency * time + modulationIndex * modulator + phaseOffset);
  53. }
  54.  
  55. // --- Wavetable Generation Function ---
  56. std::vector<std::vector<float>> generateFMWavetable(
  57. float baseFrequency,
  58. const std::vector<std::pair<float, float>>& operators, // Frequency, Modulation Index
  59. const std::vector<std::tuple<LFOType, float, float>>& lfos = {} // type, frequency, amplitude
  60. ) {
  61. std::vector<std::vector<float>> wavetable(NUM_FRAMES, std::vector<float>(SAMPLES_PER_FRAME, 0.0f));
  62.  
  63. for (int frame = 0; frame < NUM_FRAMES; ++frame) {
  64. for (int sample = 0; sample < SAMPLES_PER_FRAME; ++sample) {
  65. float time = static_cast<float>(sample) / SAMPLES_PER_FRAME;
  66. float frameProgress = static_cast<float>(frame) / (NUM_FRAMES - 1); // 0 to 1
  67.  
  68. // Apply LFOs
  69. float lfoValue = 0.0;
  70. for (const auto& lfo_params : lfos)
  71. {
  72. LFOType type;
  73. float lfoFreq, lfoAmp;
  74. std::tie(type, lfoFreq, lfoAmp) = lfo_params;
  75. lfoValue += lfoAmp * lfo(TWO_PI * lfoFreq * frameProgress, type); //LFO applied to frames
  76. }
  77.  
  78. // Start with the base frequency
  79. float currentSample = 0.0f;
  80.  
  81. // Chain the operators for FM synthesis
  82. float modulator = 0.0; // Initialize modulator
  83. for (size_t i = 0; i < operators.size(); ++i)
  84. {
  85. float opFreq = operators[i].first;
  86. float opModIndex = operators[i].second;
  87.  
  88. //apply lfoValue to either frequency or index
  89. opFreq = opFreq * (1 + lfoValue); // Example frequency modulation by LFO
  90. //opModIndex = opModIndex * (1.0 + lfoValue); // or modulate modulation index
  91. if (i == 0) //first operator is modulated by time.
  92. {
  93. modulator = fmOperator(time, opFreq, opModIndex, 0); //first op can only have phase mod
  94. }
  95. else
  96. {
  97. //apply previous modulator value
  98. modulator = fmOperator(time, opFreq, opModIndex, modulator);
  99. }
  100.  
  101. if (i == operators.size() - 1) //if we've reach the final op, assign the output value
  102. {
  103. currentSample = modulator;
  104. }
  105. }
  106.  
  107. wavetable[frame][sample] = currentSample;
  108. }
  109. }
  110. return wavetable;
  111. }
  112. // --- Wavetable Generation Function (with Controlled Randomization) ---
  113. std::vector<std::vector<float>> generateRandomFMWavetable() {
  114. std::vector<std::vector<float>> wavetable(NUM_FRAMES, std::vector<float>(SAMPLES_PER_FRAME, 0.0f));
  115.  
  116. std::random_device rd;
  117. std::mt19937 gen(rd());
  118. std::uniform_real_distribution<> base_freq_dist(50.0, 400.0); // Narrower base frequency range
  119. std::uniform_real_distribution<> freq_ratio_dist(0.5, 4.0); // Frequency ratio (around harmonic series)
  120. std::uniform_real_distribution<> mod_index_dist(0.0, 10.0); // Reduced max modulation index
  121. std::uniform_real_distribution<> lfo_freq_dist(0.005, 0.5); // Slower LFOs for frame modulation
  122. std::uniform_real_distribution<> lfo_amp_dist(0.0, 0.5); // Reduced LFO amplitude
  123. std::uniform_int_distribution<> lfo_type_dist(0, static_cast<int>(LFOType::NUM_LFO_TYPES) - 1);
  124. std::uniform_int_distribution<> num_operators_dist(2, 4); // Slightly fewer operators
  125. std::uniform_int_distribution<> num_lfos_dist(0, 2); // Fewer LFOs
  126.  
  127. float baseFrequency = base_freq_dist(gen);
  128. int numOperators = num_operators_dist(gen);
  129. int numLFOs = num_lfos_dist(gen);
  130.  
  131. std::vector<std::pair<float, float>> operators;
  132. for (int i = 0; i < numOperators; ++i) {
  133. // Key Change: Operator frequencies are *ratios* of the base frequency
  134. float freqRatio = freq_ratio_dist(gen);
  135. // Bias towards harmonic ratios (integers and simple fractions)
  136. if (gen() % 2 == 0) { // 50% chance of being near a harmonic
  137. freqRatio = std::round(freqRatio * 2.0) / 2.0; // Round to nearest 0.5
  138. }
  139. operators.emplace_back(baseFrequency * freqRatio, mod_index_dist(gen));
  140. }
  141.  
  142. std::vector<std::tuple<LFOType, float, float>> lfos;
  143. for (int i = 0; i < numLFOs; ++i) {
  144. lfos.emplace_back(
  145. static_cast<LFOType>(lfo_type_dist(gen)),
  146. lfo_freq_dist(gen),
  147. lfo_amp_dist(gen)
  148. );
  149. }
  150.  
  151. for (int frame = 0; frame < NUM_FRAMES; ++frame) {
  152. for (int sample = 0; sample < SAMPLES_PER_FRAME; ++sample) {
  153. float time = static_cast<float>(sample) / SAMPLES_PER_FRAME;
  154. float frameProgress = static_cast<float>(frame) / (NUM_FRAMES - 1);
  155.  
  156. float lfoValue = 0.0;
  157. for (const auto& lfo_params : lfos) {
  158. LFOType type;
  159. float lfoFreq, lfoAmp;
  160. std::tie(type, lfoFreq, lfoAmp) = lfo_params;
  161. lfoValue += lfoAmp * lfo(TWO_PI * lfoFreq * frameProgress, type);
  162. }
  163.  
  164. float currentSample = 0.0f;
  165. float modulator = 0.0;
  166. for (size_t i = 0; i < operators.size(); ++i) {
  167. float opFreq = operators[i].first;
  168. float opModIndex = operators[i].second;
  169. opFreq = opFreq * (1.0 + lfoValue); // Apply LFO to frequency
  170. if (i == 0) {
  171. modulator = fmOperator(time, opFreq, opModIndex, 0);
  172. } else {
  173. modulator = fmOperator(time, opFreq, opModIndex, modulator);
  174. }
  175. if (i == operators.size() - 1) {
  176. currentSample = modulator;
  177. }
  178. }
  179. wavetable[frame][sample] = currentSample;
  180. }
  181. }
  182. return wavetable;
  183. }
  184. void writeWavetableToFile(const std::vector<std::vector<float>>& wavetable, const char* filename) {
  185. std::ofstream outputFile(filename, std::ios::binary);
  186. if (!outputFile) {
  187. std::cerr << "Error opening file for writing." << std::endl;
  188. return;
  189. }
  190.  
  191. // Corrected hex header as an array of unsigned chars
  192. unsigned char header[] = {
  193. 0x52, 0x49, 0x46, 0x46, 0x80, 0x00, 0x20, 0x00, 0x57, 0x41, 0x56, 0x45, 0x4A, 0x55, 0x4E, 0x4B,
  194. 0x1C,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
  195. 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x66,0x6D,0x74,0x20,0x10,0x00,
  196. 0x00, 0x00, 0x03, 0x00, 0x01, 0x00,
  197. 0x44, 0xAC, 0x00, 0x00, 0x10, 0xB1, 0x02, 0x00, 0x04, 0x00, 0x20, 0x00, 0x63, 0x6C, 0x6D, 0x20,
  198. 0x30, 0x00, 0x00, 0x00, 0x3C, 0x21, 0x3E, 0x32, 0x30, 0x34, 0x38, 0x20, 0x30, 0x30, 0x30, 0x30,
  199. 0x30, 0x30, 0x30, 0x30, 0x20, 0x77, 0x61, 0x76, 0x65, 0x74, 0x61, 0x62, 0x6C, 0x65, 0x20, 0x28,
  200. 0x77, 0x77, 0x77, 0x2E, 0x78, 0x66, 0x65, 0x72, 0x72, 0x65, 0x63, 0x6F, 0x72, 0x64, 0x73, 0x2E,
  201. 0x63, 0x6F, 0x6D, 0x29, 0x64, 0x61, 0x74, 0x61, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00,
  202. 0x00, 0x00, 0x00, 0x00
  203. };
  204.  
  205. outputFile.write(reinterpret_cast<const char*>(header), sizeof(header));
  206.  
  207. // Write audio data
  208. for (const auto& frame : wavetable) {
  209. outputFile.write(reinterpret_cast<const char*>(frame.data()), frame.size() * sizeof(float));
  210. }
  211. outputFile.close();
  212. }
  213.  
  214. // --- Function to Check for Repetition ---
  215. bool isWavetableRepeating(const std::vector<std::vector<float>>& wavetable, float tolerance = 1e-4f) {
  216. if (wavetable.empty() || wavetable[0].empty()) {
  217. return false; // Empty wavetable is not considered repeating
  218. }
  219.  
  220. const auto& firstFrame = wavetable[0];
  221. for (size_t frame = 1; frame < wavetable.size(); ++frame) {
  222. float maxDiff = 0.0f;
  223. for (size_t sample = 0; sample < firstFrame.size(); ++sample) {
  224. maxDiff = std::max(maxDiff, std::abs(wavetable[frame][sample] - firstFrame[sample]));
  225. }
  226. if (maxDiff > tolerance) {
  227. return false; // Significant difference found, not repeating
  228. }
  229. }
  230. return true; // All frames are very similar to the first frame
  231. }
  232. // --- Function to Calculate Spectral Flatness ---
  233. float calculateSpectralFlatness(const std::vector<std::vector<float>>& wavetable) {
  234. if (wavetable.empty() || wavetable[0].empty()) {
  235. return 0.0f; // Handle empty wavetable
  236. }
  237.  
  238. std::vector<float> spectrum;
  239. for (const auto& frame : wavetable)
  240. {
  241. //perform fft on frame, and take the magnitudes.
  242. //in a real scenario, we'd use an FFT library like FFTW
  243. //here, we're going to *approximate* the spectrum using a
  244. //Discrete Cosine Transform (DCT-II), which is simpler to
  245. //implement and gives us a reasonable idea of frequency content
  246. //for real-valued signals.
  247. for (int k = 0; k < SAMPLES_PER_FRAME; ++k)
  248. {
  249. float sum = 0.0;
  250. for (int n = 0; n < SAMPLES_PER_FRAME; ++n)
  251. {
  252. sum += frame[n] * cos(M_PI / SAMPLES_PER_FRAME * (n + 0.5) * k);
  253. }
  254. spectrum.push_back(std::abs(sum)); // Magnitude
  255. }
  256. }
  257.  
  258. // Ensure no zero values in the spectrum for geometric mean calculation
  259. for (float& val : spectrum) {
  260. if (val <= 0.0f) {
  261. val = 1e-6f; // Replace with a small positive value
  262. }
  263. }
  264.  
  265. // Calculate geometric mean
  266. double geometricMean = 1.0;
  267. for (float val : spectrum) {
  268. geometricMean *= val;
  269. }
  270. geometricMean = pow(geometricMean, 1.0 / spectrum.size());
  271.  
  272.  
  273. // Calculate arithmetic mean
  274. double arithmeticMean = std::accumulate(spectrum.begin(), spectrum.end(), 0.0) / spectrum.size();
  275.  
  276. // Calculate spectral flatness (avoid division by zero)
  277. if (arithmeticMean <= 0.0) {
  278. return 0.0f; // If arithmetic mean is zero or negative, flatness is zero
  279. }
  280. return static_cast<float>(geometricMean / arithmeticMean);
  281. }
  282.  
  283. // --- Function to Calculate Average Absolute Difference (AAD) ---
  284. float calculateAverageAbsoluteDifference(const std::vector<std::vector<float>>& wavetable) {
  285. if (wavetable.empty() || wavetable[0].empty()) {
  286. return 0.0f; // Handle empty wavetable
  287. }
  288.  
  289. double totalDifference = 0.0;
  290. long long numDifferences = 0; // Use long long to prevent potential overflow
  291.  
  292. for (const auto& frame : wavetable) {
  293. for (size_t i = 0; i < frame.size() - 1; ++i) {
  294. totalDifference += std::abs(frame[i + 1] - frame[i]);
  295. numDifferences++;
  296. }
  297. }
  298. // Avoid division by zero
  299. if (numDifferences == 0) {
  300. return 0.0f;
  301. }
  302.  
  303. return static_cast<float>(totalDifference / numDifferences);
  304. }
  305.  
  306. int main() {
  307. std::random_device rd;
  308.  
  309. std::vector<std::vector<float>> randomWavetable;
  310. int maxAttempts = 100;
  311. int attempts = 0;
  312. float aadThreshold = 0.7f;
  313.  
  314. do {
  315. randomWavetable = generateRandomFMWavetable();
  316. attempts++;
  317. if (attempts > maxAttempts) {
  318. std::cerr << "Failed to generate a suitable wavetable after " << maxAttempts << " attempts." << std::endl;
  319. return 1;
  320. }
  321. std::cout << "Attempt " <<std::to_string(attempts)<< "\n";
  322. // Check for repetition *and* excessive flatness (noise)
  323. } while (isWavetableRepeating(randomWavetable) || calculateAverageAbsoluteDifference(randomWavetable) > aadThreshold);
  324.  
  325. writeWavetableToFile(randomWavetable, "random_wavetable.wav");
  326. std::cout << "Random, non-repeating, non-noisy wavetable created: random_wavetable.wav" << std::endl;
  327. std::cout << "Generated in " << attempts << " attempts." << std::endl;
  328.  
  329. return 0;
  330. }
  331.  
  332.  
  333.  
  334.  
  335. int main_old() {
  336. // Define a carrier frequency for the examples.
  337. const float carrierFrequency = 440.0f;
  338.  
  339. // Example 1: Simple 2-operator FM
  340. std::vector<std::pair<float, float>> operators1 = {
  341. {carrierFrequency, 0.0f}, // Carrier: freq, mod index
  342. {carrierFrequency * 0.5f, 5.0f} // Modulator: freq, mod index
  343. };
  344. auto wavetable1 = generateFMWavetable(carrierFrequency, operators1);
  345. writeWavetableToFile(wavetable1, "wavetable_2op.wav");
  346.  
  347.  
  348.  
  349. // Example 2: 3-operator FM with an LFO on the modulator frequency
  350. std::vector<std::pair<float, float>> operators2 = {
  351. {110.0f, 0.0f}, // Carrier (A2)
  352. {220.0f, 10.0f}, // Modulator 1
  353. {55.0f, 20.0f} // Modulator 2
  354. };
  355. //LFO, freq, amp
  356. std::vector<std::tuple<LFOType, float, float>> lfos2 = {
  357. {LFOType::SINE, 0.5f, 0.2f} // Sine LFO at 0.5 Hz, amplitude 0.2
  358. };
  359. auto wavetable2 = generateFMWavetable(110.0f, operators2, lfos2); // Base freq of A2
  360. writeWavetableToFile(wavetable2, "wavetable_3op_lfo.wav");
  361.  
  362.  
  363. // Example 3: More complex FM with multiple LFOs
  364. std::vector<std::pair<float, float>> operators3 = {
  365. {220.0f, 0.0f}, // Carrier
  366. {440.0f, 5.0f}, // Mod 1
  367. {110.0f, 10.0f}, // Mod 2
  368. {55.0f, 15.0f} // Mod 3
  369. };
  370.  
  371. std::vector<std::tuple<LFOType, float, float>> lfos3 = {
  372. {LFOType::SAW_UP, 1.0f, 0.3f}, // Saw up LFO (affects frame progression)
  373. {LFOType::TRIANGLE, 0.2f, 0.1f}, // Triangle LFO
  374. {LFOType::COSINE, 0.7f, 0.25f} // Cosine LFO
  375. };
  376.  
  377. auto wavetable3 = generateFMWavetable(220.0f, operators3, lfos3);
  378. writeWavetableToFile(wavetable3, "wavetable_complex_lfo.wav");
  379.  
  380. // Example 4: Simple sine wavetable
  381. std::vector<std::pair<float, float>> operators4 = {
  382. {440.0f, 0.0f}, // A4
  383. };
  384. auto wavetable4 = generateFMWavetable(440.0f, operators4);
  385. writeWavetableToFile(wavetable4, "wavetable_sine.wav");
  386.  
  387. // Example 5: Test all LFO types. Use a single operator, but modulate it with ALL the LFOs
  388. std::vector<std::pair<float, float>> operators5 = {
  389. {110.0f, 0.0f} // A2
  390. };
  391.  
  392. std::vector<std::tuple<LFOType, float, float>> lfos5 = {
  393. {LFOType::SINE, 1.0f, 0.2f}, // Sine
  394. {LFOType::COSINE, 0.5f, 0.3f}, // Cosine
  395. {LFOType::SAW_UP, 0.2f, 0.4f}, // Saw Up
  396. {LFOType::SAW_DOWN, 0.1f, 0.5f}, // Saw Down
  397. {LFOType::TRIANGLE, 0.8f, 0.15f}, // Triangle
  398. {LFOType::SQUARE, 2.0f, 0.25f} // Square
  399. };
  400.  
  401. auto wavetable5 = generateFMWavetable(110.0f, operators5, lfos5);
  402. writeWavetableToFile(wavetable5, "wavetable_all_lfos.wav");
  403.  
  404. std::cout << "Wavetable files created successfully." << std::endl;
  405.  
  406. return 0;
  407. }
Advertisement
Add Comment
Please, Sign In to add comment