From dea9549d44f6114c46816613a1161b43b84b7008 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maksymilian=20Wilczy=C5=84ski?= Date: Wed, 2 Apr 2025 22:37:14 +0200 Subject: [PATCH] first commit --- .gitignore | 4 + synthetizer.cpp | 200 ++++++++++++++++++++++++++++++++++++ synthetizer.sln | 31 ++++++ synthetizer.vcxproj | 138 +++++++++++++++++++++++++ synthetizer.vcxproj.filters | 25 +++++ waves.txt | 3 + 6 files changed, 401 insertions(+) create mode 100644 .gitignore create mode 100644 synthetizer.cpp create mode 100644 synthetizer.sln create mode 100644 synthetizer.vcxproj create mode 100644 synthetizer.vcxproj.filters create mode 100644 waves.txt diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..827658e --- /dev/null +++ b/.gitignore @@ -0,0 +1,4 @@ +.vs/ +x64/ +*.wav +*.vcxproj.user \ No newline at end of file diff --git a/synthetizer.cpp b/synthetizer.cpp new file mode 100644 index 0000000..d943310 --- /dev/null +++ b/synthetizer.cpp @@ -0,0 +1,200 @@ +#include +#include +#include +#include +#include +#include + +const int SAMPLE_RATE = 44100; +const double TWO_PI = 6.283185307179586476925286766559; + +// Enum for waveform types +enum Waveform { + SINE, + TRIANGLE, + SAWTOOTH, + SQUARE +}; + +// Structure to hold the waveform parameters +struct Wave { + Waveform type; + int start_ms; + int duration_ms; + int frequency; + double volume; // New field for volume control (0.0 to 1.0) +}; + +// Read the parameters from the text file +bool loadWaveParams(const std::string& filename, std::vector& waves, int& totalDuration) { + std::ifstream file(filename); + if (!file.is_open()) { + std::cerr << "Error opening file!" << std::endl; + return false; + } + + std::string line; + while (std::getline(file, line)) { + if (line.find("TOTAL_DURATION") != std::string::npos) { + size_t startPos = line.find("(") + 1; + size_t endPos = line.find(")"); + totalDuration = std::stoi(line.substr(startPos, endPos - startPos)); + } + else if (line.find("SINE") != std::string::npos || line.find("SAWTOOTH") != std::string::npos || + line.find("SQUARE") != std::string::npos || line.find("TRIANGLE") != std::string::npos) { + std::istringstream ss(line); + std::string waveType; + int start_ms, duration_ms, frequency; + double volume; + ss >> waveType >> start_ms >> duration_ms >> frequency >> volume; + + Wave wave; + wave.start_ms = start_ms; + wave.duration_ms = duration_ms; + wave.frequency = frequency; + wave.volume = volume; + + if (waveType == "SINE") { + wave.type = SINE; + } + else if (waveType == "SAWTOOTH") { + wave.type = SAWTOOTH; + } + else if (waveType == "SQUARE") { + wave.type = SQUARE; + } + else if (waveType == "TRIANGLE") { + wave.type = TRIANGLE; + } + + waves.push_back(wave); + } + } + + return true; +} + +// Waveform generation functions +double sineWave(double frequency, double time) { + return std::sin(TWO_PI * frequency * time); +} + +double triangleWave(double frequency, double time) { + double period = 1.0 / frequency; + double phase = fmod(time, period) / period; // Normalized phase (0 to 1) + if (phase < 0.5) { + return 4 * phase - 1; // Rising part of the triangle + } + else { + return 3 - 4 * phase; // Falling part of the triangle + } +} + +double sawtoothWave(double frequency, double time) { + double period = 1.0 / frequency; + double phase = fmod(time, period) / period; // Normalized phase (0 to 1) + return 2 * phase - 1; // Ramp from -1 to 1 +} + +double squareWave(double frequency, double time) { + double period = 1.0 / frequency; + double phase = fmod(time, period) / period; // Normalized phase (0 to 1) + return phase < 0.5 ? 1.0 : -1.0; // 1 for the first half, -1 for the second half +} + +// Mix the waveforms with volume control +double generateWave(Waveform waveform, double frequency, double time) { + switch (waveform) { + case SINE: + return sineWave(frequency, time); + case TRIANGLE: + return triangleWave(frequency, time); + case SAWTOOTH: + return sawtoothWave(frequency, time); + case SQUARE: + return squareWave(frequency, time); + default: + return sineWave(frequency, time); // Default to sine wave + } +} + +std::vector generateAudio(const std::vector& waves, int totalDuration) { + std::vector samples(SAMPLE_RATE * totalDuration / 1000, 0); // Initialize with zeros + + // Process each wave + for (const auto& wave : waves) { + int startSample = wave.start_ms * SAMPLE_RATE / 1000; + int endSample = (wave.start_ms + wave.duration_ms) * SAMPLE_RATE / 1000; + + // Generate the waveform for the specified duration + for (int t = startSample; t < endSample; ++t) { + double time = static_cast(t) / SAMPLE_RATE; + double sample = generateWave(wave.type, wave.frequency, time); + sample *= wave.volume; // Apply the volume to the waveform + + samples[t] += static_cast(sample * 32767); // Linear mixing + } + } + + return samples; +} + +// Write WAV file +void writeWav(const std::string& filename, const std::vector& samples) { + std::ofstream file(filename, std::ios::binary); + + // WAV Header + struct WAVHeader { + char riff[4]; // "RIFF" + uint32_t fileSize; + char wave[4]; // "WAVE" + char fmt[4]; // "fmt " + uint32_t fmtSize; + uint16_t audioFormat; + uint16_t numChannels; + uint32_t sampleRate; + uint32_t byteRate; + uint16_t blockAlign; + uint16_t bitsPerSample; + char data[4]; // "data" + uint32_t dataSize; + }; + + WAVHeader header; + memcpy(header.riff, "RIFF", 4); + header.fileSize = 36 + samples.size() * sizeof(int16_t); + memcpy(header.wave, "WAVE", 4); + memcpy(header.fmt, "fmt ", 4); + header.fmtSize = 16; + header.audioFormat = 1; // PCM + header.numChannels = 1; // Mono + header.sampleRate = SAMPLE_RATE; + header.byteRate = SAMPLE_RATE * sizeof(int16_t); + header.blockAlign = sizeof(int16_t); + header.bitsPerSample = 16; + memcpy(header.data, "data", 4); + header.dataSize = samples.size() * sizeof(int16_t); + + file.write(reinterpret_cast(&header), sizeof(header)); + file.write(reinterpret_cast(samples.data()), samples.size() * sizeof(int16_t)); +} + +int main() { + std::vector waves; + int totalDuration = 0; + + // Load the wave parameters from a text file + if (!loadWaveParams("waves.txt", waves, totalDuration)) { + std::cerr << "Failed to load wave parameters!" << std::endl; + return 1; + } + + // Generate the audio by mixing the waveforms + std::vector samples = generateAudio(waves, totalDuration); + + // Write the audio to a WAV file + writeWav("output_mixed_with_volume.wav", samples); + + std::cout << "WAV file generated: output_mixed_with_volume.wav" << std::endl; + return 0; +} diff --git a/synthetizer.sln b/synthetizer.sln new file mode 100644 index 0000000..caa8af1 --- /dev/null +++ b/synthetizer.sln @@ -0,0 +1,31 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 17 +VisualStudioVersion = 17.13.35825.156 d17.13 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "synthetizer", "synthetizer.vcxproj", "{E3DCEF34-0A95-49D2-84D9-5FB1BE6F75F1}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|x64 = Debug|x64 + Debug|x86 = Debug|x86 + Release|x64 = Release|x64 + Release|x86 = Release|x86 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {E3DCEF34-0A95-49D2-84D9-5FB1BE6F75F1}.Debug|x64.ActiveCfg = Debug|x64 + {E3DCEF34-0A95-49D2-84D9-5FB1BE6F75F1}.Debug|x64.Build.0 = Debug|x64 + {E3DCEF34-0A95-49D2-84D9-5FB1BE6F75F1}.Debug|x86.ActiveCfg = Debug|Win32 + {E3DCEF34-0A95-49D2-84D9-5FB1BE6F75F1}.Debug|x86.Build.0 = Debug|Win32 + {E3DCEF34-0A95-49D2-84D9-5FB1BE6F75F1}.Release|x64.ActiveCfg = Release|x64 + {E3DCEF34-0A95-49D2-84D9-5FB1BE6F75F1}.Release|x64.Build.0 = Release|x64 + {E3DCEF34-0A95-49D2-84D9-5FB1BE6F75F1}.Release|x86.ActiveCfg = Release|Win32 + {E3DCEF34-0A95-49D2-84D9-5FB1BE6F75F1}.Release|x86.Build.0 = Release|Win32 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {925453FC-5350-4A69-AE70-D2A160CB7CA2} + EndGlobalSection +EndGlobal diff --git a/synthetizer.vcxproj b/synthetizer.vcxproj new file mode 100644 index 0000000..6c7d13f --- /dev/null +++ b/synthetizer.vcxproj @@ -0,0 +1,138 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + 17.0 + Win32Proj + {e3dcef34-0a95-49d2-84d9-5fb1be6f75f1} + synthetizer + 10.0 + + + + Application + true + v143 + Unicode + + + Application + false + v143 + true + Unicode + + + Application + true + v143 + Unicode + + + Application + false + v143 + true + Unicode + + + + + + + + + + + + + + + + + + + + + + Level3 + true + WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + + + Console + true + + + + + Level3 + true + true + true + WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + + + Console + true + true + true + + + + + Level3 + true + _DEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + + + Console + true + + + + + Level3 + true + true + true + NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + + + Console + true + true + true + + + + + + + + + + + + \ No newline at end of file diff --git a/synthetizer.vcxproj.filters b/synthetizer.vcxproj.filters new file mode 100644 index 0000000..eca0fe8 --- /dev/null +++ b/synthetizer.vcxproj.filters @@ -0,0 +1,25 @@ + + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;c++;cppm;ixx;def;odl;idl;hpj;bat;asm;asmx + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;hh;hpp;hxx;h++;hm;inl;inc;ipp;xsd + + + {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms + + + + + Pliki źródłowe + + + + + + \ No newline at end of file diff --git a/waves.txt b/waves.txt new file mode 100644 index 0000000..9e8b21e --- /dev/null +++ b/waves.txt @@ -0,0 +1,3 @@ +TOTAL_DURATION(5000) // Total duration in milliseconds +SINE 0 1000 440 1.0 // Sine wave starts at 0 ms, lasts for 1000 ms, frequency 440 Hz, full volume +SQUARE 0 1000 440 0.5 // Square wave starts at 0 ms, lasts for 1000 ms, frequency 440 Hz, 50% volume