201 lines
6.4 KiB
C++
201 lines
6.4 KiB
C++
#include <iostream>
|
|
#include <fstream>
|
|
#include <cmath>
|
|
#include <vector>
|
|
#include <sstream>
|
|
#include <string>
|
|
|
|
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<Wave>& 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<int16_t> generateAudio(const std::vector<Wave>& waves, int totalDuration) {
|
|
std::vector<int16_t> 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<double>(t) / SAMPLE_RATE;
|
|
double sample = generateWave(wave.type, wave.frequency, time);
|
|
sample *= wave.volume; // Apply the volume to the waveform
|
|
|
|
samples[t] += static_cast<int16_t>(sample * 32767); // Linear mixing
|
|
}
|
|
}
|
|
|
|
return samples;
|
|
}
|
|
|
|
// Write WAV file
|
|
void writeWav(const std::string& filename, const std::vector<int16_t>& 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<char*>(&header), sizeof(header));
|
|
file.write(reinterpret_cast<const char*>(samples.data()), samples.size() * sizeof(int16_t));
|
|
}
|
|
|
|
int main() {
|
|
std::vector<Wave> 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<int16_t> 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;
|
|
}
|