diff --git a/synthetizer.cpp b/synthetizer.cpp index 7dc6134..6c951f1 100644 --- a/synthetizer.cpp +++ b/synthetizer.cpp @@ -61,68 +61,122 @@ bool loadWaveParams(const std::string& filename, std::vector& waves, int& std::string line; bool inCommentBlock = false; + while (std::getline(file, line)) { - // Remove C-style comments + // Handle multiline /* comments */ if (inCommentBlock) { - size_t endComment = line.find("*/"); - if (endComment != std::string::npos) { + size_t end = line.find("*/"); + if (end != std::string::npos) { inCommentBlock = false; - line = line.substr(endComment + 2); // Remaining part of the line after the comment + line = line.substr(end + 2); } else { - continue; // Skip entire line if still in a comment block + continue; } } - size_t startComment = line.find("/*"); - while (startComment != std::string::npos) { - size_t endComment = line.find("*/", startComment + 2); - if (endComment != std::string::npos) { - line.erase(startComment, endComment - startComment + 2); // Remove the comment - startComment = line.find("/*"); // Look for another comment + while (true) { + size_t start = line.find("/*"); + if (start != std::string::npos) { + size_t end = line.find("*/", start + 2); + if (end != std::string::npos) { + line.erase(start, end - start + 2); // Remove /* ... */ + } + else { + line = line.substr(0, start); // Cut off everything after /* + inCommentBlock = true; + break; + } } else { - inCommentBlock = true; - line = line.substr(0, startComment); // Remove everything after /* till end of line break; } } - // Strip whitespace + // Trim leading whitespace line.erase(0, line.find_first_not_of(" \t\r\n")); if (line.empty() || line[0] == '#') continue; - // Check if the line defines the total duration - 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)); + // Handle TOTAL_DURATION + if (line.rfind("TOTAL_DURATION", 0) == 0) { + std::istringstream durationStream(line); + std::string keyword; + durationStream >> keyword >> totalDuration; continue; } - // Initialize variables for waveform properties std::istringstream ss(line); std::string waveType; - int start_ms, duration_ms, frequency; + ss >> waveType; + + if (waveType.empty()) continue; + + Wave wave; + wave.type = SINE; + + 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; + else if (waveType == "NOISE") wave.type = NOISE; + else if (waveType == "DPCM") wave.type = DPCM; + else { + std::cout << "Unknown waveform type: " << waveType << std::endl; + continue; + } + + int start_ms, duration_ms; + ss >> start_ms >> duration_ms; + wave.start_ms = start_ms; + wave.duration_ms = duration_ms; + + if (wave.type == DPCM) { + std::string token; + while (ss >> token && token == "/*") { + while (ss >> token && token != "*/"); + } + + if (token.empty()) { + std::cout << "Missing sample path for DPCM at " << start_ms << "ms\n"; + continue; + } + + // Optional volume + if (!(ss >> wave.volume)) { + wave.volume = 1.0; + } + + wave.samplePath = token; + wave.frequency = 0; + wave.dutyCycle = 0.5; + wave.sweepDepth = 0.0; + wave.sweepRate = 0.0; + wave.attack = 0.01; + wave.decay = 0.2; + + if (!loadWavFile(wave.samplePath, wave.sampleData)) { + std::cout << "Failed to load sample: " << wave.samplePath << std::endl; + } + + waves.push_back(wave); + continue; + } + + // Standard wave parameters double volume = 1.0; double duty = 0.5; double sweepDepth = 0.0, sweepRate = 0.0; double attack = 0.01, decay = 0.2; + int frequency; - // Read the basic wave parameters - ss >> waveType >> start_ms >> duration_ms >> frequency >> volume; - - // Set default values if optional parameters are missing + ss >> frequency; + ss >> volume; if (!(ss >> duty)) duty = 0.5; if (!(ss >> sweepDepth)) sweepDepth = 0.0; if (!(ss >> sweepRate)) sweepRate = 0.0; if (!(ss >> attack)) attack = 0.01; if (!(ss >> decay)) decay = 0.2; - // Create a new Wave object and set its properties - Wave wave; - wave.start_ms = start_ms; - wave.duration_ms = duration_ms; wave.frequency = frequency; wave.volume = volume; wave.dutyCycle = duty; @@ -131,25 +185,6 @@ bool loadWaveParams(const std::string& filename, std::vector& waves, int& wave.attack = attack; wave.decay = decay; - // Determine wave type and set corresponding enum - 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; - else if (waveType == "NOISE") wave.type = NOISE; - else if (waveType == "DPCM") { - wave.type = DPCM; - ss >> wave.samplePath; // Read sample file path for DPCM waves - if (!loadWavFile(wave.samplePath, wave.sampleData)) { - std::cout << "Failed to load sample: " << wave.samplePath << std::endl; - } - } - else { - std::cout << "Unknown waveform type: " << waveType << std::endl; - continue; // Skip unknown waveform types - } - - // Add the wave to the vector waves.push_back(wave); } diff --git a/waves.txt b/waves.txt index 1b7288b..3e431bd 100644 --- a/waves.txt +++ b/waves.txt @@ -1,6 +1,7 @@ -TOTAL_DURATION(3000) // 3 seconds total +TOTAL_DURATION 4500 // extended to fit all waves cleanly -SINE 0 1000 440 1.0 0 3 5 0.05 0.3 // sine with slow vibrato and long decay -SQUARE 500 1000 330 0.7 0.25 6 4 0.01 0.2 // square with duty cycle and sweep -NOISE 1500 400 0 0.8 0 0 0 0.02 0.1 // noise burst -DPCM 2000 800 /* boilerplate */ 0 0 0 0 0 0 1.0 snare.wav \ No newline at end of file +SINE 0 500 440 1.0 0 3 5 0.05 0.3 // sine with slow vibrato and long decay +TRIANGLE 1000 500 550 0.9 0.5 2 3 0.02 0.25 // triangle with sweep +SQUARE 2000 500 330 0.7 0.25 6 4 0.01 0.2 // square with duty cycle and sweep +NOISE 3000 300 0 0.8 0 0 0 0.02 0.1 // noise burst +DPCM 3900 500 snare.wav 1.0 // dpcm sample 1.0 volume