add multiple features
1. dpcm (temporary boilerplate needed, sorry) 2. noise 3. volume 4. multiline comments
This commit is contained in:
1 parent
164d2a37d9
commit
9bc043a159
5 files changed
+205
-108
No files matched your search
+1
-1
@@ -1,4 +1,4 @@
|
|||||||
.vs/
|
.vs/
|
||||||
x64/
|
x64/
|
||||||
*.wav
|
output.wav
|
||||||
*.vcxproj.user
|
*.vcxproj.user
|
||||||
+191
-47
@@ -1,5 +1,6 @@
|
|||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <fstream>
|
#include <fstream>
|
||||||
|
#include <algorithm>
|
||||||
#include <cmath>
|
#include <cmath>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
#include <sstream>
|
#include <sstream>
|
||||||
@@ -7,13 +8,17 @@
|
|||||||
|
|
||||||
const int SAMPLE_RATE = 44100;
|
const int SAMPLE_RATE = 44100;
|
||||||
const double M_TAU = 6.283185307179586476925286766559;
|
const double M_TAU = 6.283185307179586476925286766559;
|
||||||
|
uint16_t lfsr = 0xACE1u;
|
||||||
|
unsigned bit;
|
||||||
|
|
||||||
// Enum for waveform types
|
// Enum for waveform types
|
||||||
enum Waveform {
|
enum Waveform {
|
||||||
SINE,
|
SINE,
|
||||||
TRIANGLE,
|
TRIANGLE,
|
||||||
SAWTOOTH,
|
SAWTOOTH,
|
||||||
SQUARE
|
SQUARE,
|
||||||
|
NOISE,
|
||||||
|
DPCM
|
||||||
};
|
};
|
||||||
|
|
||||||
// Structure to hold the waveform parameters
|
// Structure to hold the waveform parameters
|
||||||
@@ -22,9 +27,30 @@ struct Wave {
|
|||||||
int start_ms;
|
int start_ms;
|
||||||
int duration_ms;
|
int duration_ms;
|
||||||
int frequency;
|
int frequency;
|
||||||
double volume; // New field for volume control (0.0 to 1.0)
|
double volume;
|
||||||
|
double dutyCycle; // For SQUARE
|
||||||
|
double sweepDepth; // Vibrato or slide
|
||||||
|
double sweepRate;
|
||||||
|
double attack;
|
||||||
|
double decay;
|
||||||
|
std::string samplePath;
|
||||||
|
std::vector<int16_t> sampleData;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
bool loadWavFile(const std::string& path, std::vector<int16_t>& outSamples) {
|
||||||
|
std::ifstream file(path, std::ios::binary);
|
||||||
|
if (!file.is_open()) return false;
|
||||||
|
|
||||||
|
file.seekg(44); // skip header
|
||||||
|
outSamples.clear();
|
||||||
|
|
||||||
|
int16_t sample;
|
||||||
|
while (file.read(reinterpret_cast<char*>(&sample), sizeof(sample))) {
|
||||||
|
outSamples.push_back(sample);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
// Read the parameters from the text file
|
// Read the parameters from the text file
|
||||||
bool loadWaveParams(const std::string& filename, std::vector<Wave>& waves, int& totalDuration) {
|
bool loadWaveParams(const std::string& filename, std::vector<Wave>& waves, int& totalDuration) {
|
||||||
std::ifstream file(filename);
|
std::ifstream file(filename);
|
||||||
@@ -34,41 +60,97 @@ bool loadWaveParams(const std::string& filename, std::vector<Wave>& waves, int&
|
|||||||
}
|
}
|
||||||
|
|
||||||
std::string line;
|
std::string line;
|
||||||
|
bool inCommentBlock = false;
|
||||||
while (std::getline(file, line)) {
|
while (std::getline(file, line)) {
|
||||||
|
// Remove C-style comments
|
||||||
|
if (inCommentBlock) {
|
||||||
|
size_t endComment = line.find("*/");
|
||||||
|
if (endComment != std::string::npos) {
|
||||||
|
inCommentBlock = false;
|
||||||
|
line = line.substr(endComment + 2); // Remaining part of the line after the comment
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
continue; // Skip entire line if still in a comment block
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
inCommentBlock = true;
|
||||||
|
line = line.substr(0, startComment); // Remove everything after /* till end of line
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Strip 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) {
|
if (line.find("TOTAL_DURATION") != std::string::npos) {
|
||||||
size_t startPos = line.find("(") + 1;
|
size_t startPos = line.find("(") + 1;
|
||||||
size_t endPos = line.find(")");
|
size_t endPos = line.find(")");
|
||||||
totalDuration = std::stoi(line.substr(startPos, endPos - startPos));
|
totalDuration = std::stoi(line.substr(startPos, endPos - startPos));
|
||||||
|
continue;
|
||||||
}
|
}
|
||||||
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;
|
// Initialize variables for waveform properties
|
||||||
wave.start_ms = start_ms;
|
std::istringstream ss(line);
|
||||||
wave.duration_ms = duration_ms;
|
std::string waveType;
|
||||||
wave.frequency = frequency;
|
int start_ms, duration_ms, frequency;
|
||||||
wave.volume = volume;
|
double volume = 1.0;
|
||||||
|
double duty = 0.5;
|
||||||
|
double sweepDepth = 0.0, sweepRate = 0.0;
|
||||||
|
double attack = 0.01, decay = 0.2;
|
||||||
|
|
||||||
if (waveType == "SINE") {
|
// Read the basic wave parameters
|
||||||
wave.type = SINE;
|
ss >> waveType >> start_ms >> duration_ms >> frequency >> volume;
|
||||||
}
|
|
||||||
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);
|
// Set default values if optional parameters are missing
|
||||||
|
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;
|
||||||
|
wave.sweepDepth = sweepDepth;
|
||||||
|
wave.sweepRate = sweepRate;
|
||||||
|
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);
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
@@ -96,43 +178,105 @@ double sawtoothWave(double frequency, double time) {
|
|||||||
return 2 * phase - 1; // Ramp from -1 to 1
|
return 2 * phase - 1; // Ramp from -1 to 1
|
||||||
}
|
}
|
||||||
|
|
||||||
double squareWave(double frequency, double time) {
|
double squareWave(double frequency, double time, double duty = 0.5) {
|
||||||
double period = 1.0 / frequency;
|
double period = 1.0 / frequency;
|
||||||
double phase = fmod(time, period) / period; // Normalized phase (0 to 1)
|
double phase = fmod(time, period) / period; // 0 to 1
|
||||||
return phase < 0.5 ? 1.0 : -1.0; // 1 for the first half, -1 for the second half
|
return phase < duty ? 1.0 : -1.0;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Mix the waveforms with volume control
|
// Mix the waveforms with volume control
|
||||||
double generateWave(Waveform waveform, double frequency, double time) {
|
double generateWave(Waveform waveform, double frequency, double time, double duty = 0.5) {
|
||||||
switch (waveform) {
|
switch (waveform) {
|
||||||
case SINE:
|
case SINE: return sineWave(frequency, time);
|
||||||
return sineWave(frequency, time);
|
case TRIANGLE: return triangleWave(frequency, time);
|
||||||
case TRIANGLE:
|
case SAWTOOTH: return sawtoothWave(frequency, time);
|
||||||
return triangleWave(frequency, time);
|
case SQUARE: return squareWave(frequency, time, duty);
|
||||||
case SAWTOOTH:
|
default: return sineWave(frequency, time);
|
||||||
return sawtoothWave(frequency, time);
|
|
||||||
case SQUARE:
|
|
||||||
return squareWave(frequency, time);
|
|
||||||
default:
|
|
||||||
return sineWave(frequency, time); // Default to sine wave
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
double applyVibrato(double baseFreq, double time, double depth = 5.0, double rate = 5.0) {
|
||||||
|
return baseFreq + depth * std::sin(M_TAU * rate * time);
|
||||||
|
}
|
||||||
|
|
||||||
|
double applyEnvelope(double time, double startTime, double duration, double volume, double attack, double decay) {
|
||||||
|
double elapsed = time - startTime;
|
||||||
|
if (elapsed < 0.0) return 0.0;
|
||||||
|
|
||||||
|
if (elapsed < attack) {
|
||||||
|
// Attack phase: ramp up from 0 to volume
|
||||||
|
return volume * (elapsed / attack);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (elapsed < (attack + decay)) {
|
||||||
|
// Decay phase: ramp down to 0
|
||||||
|
return volume * (1.0 - ((elapsed - attack) / decay));
|
||||||
|
}
|
||||||
|
|
||||||
|
// After envelope ends
|
||||||
|
return 0.0;
|
||||||
|
}
|
||||||
|
|
||||||
|
double noiseWave() {
|
||||||
|
// 16-bit Galois LFSR
|
||||||
|
bit = ((lfsr >> 0) ^ (lfsr >> 2) ^ (lfsr >> 3) ^ (lfsr >> 5)) & 1;
|
||||||
|
lfsr = (lfsr >> 1) | (bit << 15);
|
||||||
|
return ((lfsr & 1) ? 1.0 : -1.0); // White-ish noise
|
||||||
|
}
|
||||||
|
|
||||||
std::vector<int16_t> generateAudio(const std::vector<Wave>& waves, int totalDuration) {
|
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
|
std::vector<int16_t> samples(SAMPLE_RATE * totalDuration / 1000, 0); // Initialize with zeros
|
||||||
|
|
||||||
// Process each wave
|
|
||||||
for (const auto& wave : waves) {
|
for (const auto& wave : waves) {
|
||||||
int startSample = wave.start_ms * SAMPLE_RATE / 1000;
|
int startSample = wave.start_ms * SAMPLE_RATE / 1000;
|
||||||
int endSample = (wave.start_ms + wave.duration_ms) * SAMPLE_RATE / 1000;
|
int endSample = (wave.start_ms + wave.duration_ms) * SAMPLE_RATE / 1000;
|
||||||
|
|
||||||
// Generate the waveform for the specified duration
|
if (wave.type == DPCM) {
|
||||||
|
// DPCM Sample Playback (raw PCM sample, assumed 16-bit mono)
|
||||||
|
for (int i = 0; i < wave.sampleData.size(); ++i) {
|
||||||
|
int t = startSample + i;
|
||||||
|
if (t >= samples.size()) break;
|
||||||
|
|
||||||
|
double envelopeVol = applyEnvelope(
|
||||||
|
t / (double)SAMPLE_RATE,
|
||||||
|
wave.start_ms / 1000.0,
|
||||||
|
wave.duration_ms / 1000.0,
|
||||||
|
1.0, wave.attack, wave.decay
|
||||||
|
);
|
||||||
|
|
||||||
|
samples[t] += static_cast<int16_t>(wave.sampleData[i] * envelopeVol);
|
||||||
|
}
|
||||||
|
continue; // Skip standard waveform generation for DPCM
|
||||||
|
}
|
||||||
|
|
||||||
for (int t = startSample; t < endSample; ++t) {
|
for (int t = startSample; t < endSample; ++t) {
|
||||||
if (t >= samples.size()) break; // Prevent out-of-bounds access
|
if (t >= samples.size()) break;
|
||||||
|
|
||||||
double time = static_cast<double>(t) / SAMPLE_RATE;
|
double time = static_cast<double>(t) / SAMPLE_RATE;
|
||||||
double sample = generateWave(wave.type, wave.frequency, time);
|
double startTime = wave.start_ms / 1000.0;
|
||||||
sample *= wave.volume; // Apply volume
|
double relTime = time - startTime;
|
||||||
samples[t] += static_cast<int16_t>(sample * 32767); // Linear mixing
|
if (relTime < 0) continue;
|
||||||
|
|
||||||
|
// Frequency sweep / vibrato
|
||||||
|
double modFreq = wave.frequency + wave.sweepDepth * std::sin(M_TAU * wave.sweepRate * time);
|
||||||
|
|
||||||
|
// Generate waveform
|
||||||
|
double rawSample = 0.0;
|
||||||
|
if (wave.type == NOISE) {
|
||||||
|
rawSample = noiseWave(); // LFSR noise
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
rawSample = generateWave(wave.type, modFreq, time, wave.dutyCycle);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Envelope application
|
||||||
|
double envelopeVolume = applyEnvelope(time, startTime, wave.duration_ms / 1000.0, wave.volume, wave.attack, wave.decay);
|
||||||
|
|
||||||
|
// Final sample
|
||||||
|
double finalSample = rawSample * envelopeVolume;
|
||||||
|
|
||||||
|
// Mix into audio buffer
|
||||||
|
samples[t] += static_cast<int16_t>(std::clamp(finalSample * 32767.0, -32768.0, 32767.0));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -76,6 +76,8 @@
|
|||||||
<SDLCheck>true</SDLCheck>
|
<SDLCheck>true</SDLCheck>
|
||||||
<PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
<PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||||
<ConformanceMode>true</ConformanceMode>
|
<ConformanceMode>true</ConformanceMode>
|
||||||
|
<LanguageStandard>stdcpp20</LanguageStandard>
|
||||||
|
<LanguageStandard_C>stdc17</LanguageStandard_C>
|
||||||
</ClCompile>
|
</ClCompile>
|
||||||
<Link>
|
<Link>
|
||||||
<SubSystem>Console</SubSystem>
|
<SubSystem>Console</SubSystem>
|
||||||
@@ -90,6 +92,8 @@
|
|||||||
<SDLCheck>true</SDLCheck>
|
<SDLCheck>true</SDLCheck>
|
||||||
<PreprocessorDefinitions>WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
<PreprocessorDefinitions>WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||||
<ConformanceMode>true</ConformanceMode>
|
<ConformanceMode>true</ConformanceMode>
|
||||||
|
<LanguageStandard>stdcpp20</LanguageStandard>
|
||||||
|
<LanguageStandard_C>stdc17</LanguageStandard_C>
|
||||||
</ClCompile>
|
</ClCompile>
|
||||||
<Link>
|
<Link>
|
||||||
<SubSystem>Console</SubSystem>
|
<SubSystem>Console</SubSystem>
|
||||||
@@ -104,6 +108,8 @@
|
|||||||
<SDLCheck>true</SDLCheck>
|
<SDLCheck>true</SDLCheck>
|
||||||
<PreprocessorDefinitions>_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
<PreprocessorDefinitions>_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||||
<ConformanceMode>true</ConformanceMode>
|
<ConformanceMode>true</ConformanceMode>
|
||||||
|
<LanguageStandard>stdcpp20</LanguageStandard>
|
||||||
|
<LanguageStandard_C>stdc17</LanguageStandard_C>
|
||||||
</ClCompile>
|
</ClCompile>
|
||||||
<Link>
|
<Link>
|
||||||
<SubSystem>Console</SubSystem>
|
<SubSystem>Console</SubSystem>
|
||||||
@@ -118,6 +124,8 @@
|
|||||||
<SDLCheck>true</SDLCheck>
|
<SDLCheck>true</SDLCheck>
|
||||||
<PreprocessorDefinitions>NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
<PreprocessorDefinitions>NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||||
<ConformanceMode>true</ConformanceMode>
|
<ConformanceMode>true</ConformanceMode>
|
||||||
|
<LanguageStandard>stdcpp20</LanguageStandard>
|
||||||
|
<LanguageStandard_C>stdc17</LanguageStandard_C>
|
||||||
</ClCompile>
|
</ClCompile>
|
||||||
<Link>
|
<Link>
|
||||||
<SubSystem>Console</SubSystem>
|
<SubSystem>Console</SubSystem>
|
||||||
|
|||||||
@@ -1,61 +1,6 @@
|
|||||||
TOTAL_DURATION(10000)
|
TOTAL_DURATION(3000) // 3 seconds total
|
||||||
|
|
||||||
SQUARE 0 400 392 0.7
|
SINE 0 1000 440 1.0 0 3 5 0.05 0.3 // sine with slow vibrato and long decay
|
||||||
SAWTOOTH 0 400 392 0.4
|
SQUARE 500 1000 330 0.7 0.25 6 4 0.01 0.2 // square with duty cycle and sweep
|
||||||
SINE 0 400 392 0.3
|
NOISE 1500 400 0 0.8 0 0 0 0.02 0.1 // noise burst
|
||||||
TRIANGLE 0 400 392 0.2
|
DPCM 2000 800 /* boilerplate */ 0 0 0 0 0 0 1.0 snare.wav
|
||||||
|
|
||||||
SQUARE 450 400 392 0.7
|
|
||||||
SAWTOOTH 450 400 392 0.4
|
|
||||||
SINE 450 400 392 0.3
|
|
||||||
TRIANGLE 450 400 392 0.2
|
|
||||||
|
|
||||||
SQUARE 900 800 440 0.7
|
|
||||||
SAWTOOTH 900 800 440 0.4
|
|
||||||
SINE 900 800 440 0.3
|
|
||||||
TRIANGLE 900 800 440 0.2
|
|
||||||
|
|
||||||
SQUARE 1800 800 392 0.7
|
|
||||||
SAWTOOTH 1800 800 392 0.4
|
|
||||||
SINE 1800 800 392 0.3
|
|
||||||
TRIANGLE 1800 800 392 0.2
|
|
||||||
|
|
||||||
SQUARE 2700 800 523 0.7
|
|
||||||
SAWTOOTH 2700 800 523 0.4
|
|
||||||
SINE 2700 800 523 0.3
|
|
||||||
TRIANGLE 2700 800 523 0.2
|
|
||||||
|
|
||||||
SQUARE 3600 1600 494 0.7
|
|
||||||
SAWTOOTH 3600 1600 494 0.4
|
|
||||||
SINE 3600 1600 494 0.3
|
|
||||||
TRIANGLE 3600 1600 494 0.2
|
|
||||||
|
|
||||||
SQUARE 5400 400 392 0.7
|
|
||||||
SAWTOOTH 5400 400 392 0.4
|
|
||||||
SINE 5400 400 392 0.3
|
|
||||||
TRIANGLE 5400 400 392 0.2
|
|
||||||
|
|
||||||
SQUARE 5850 400 392 0.7
|
|
||||||
SAWTOOTH 5850 400 392 0.4
|
|
||||||
SINE 5850 400 392 0.3
|
|
||||||
TRIANGLE 5850 400 392 0.2
|
|
||||||
|
|
||||||
SQUARE 6300 800 440 0.7
|
|
||||||
SAWTOOTH 6300 800 440 0.4
|
|
||||||
SINE 6300 800 440 0.3
|
|
||||||
TRIANGLE 6300 800 440 0.2
|
|
||||||
|
|
||||||
SQUARE 7200 800 392 0.7
|
|
||||||
SAWTOOTH 7200 800 392 0.4
|
|
||||||
SINE 7200 800 392 0.3
|
|
||||||
TRIANGLE 7200 800 392 0.2
|
|
||||||
|
|
||||||
SQUARE 8100 800 587 0.7
|
|
||||||
SAWTOOTH 8100 800 587 0.4
|
|
||||||
SINE 8100 800 587 0.3
|
|
||||||
TRIANGLE 8100 800 587 0.2
|
|
||||||
|
|
||||||
SQUARE 9000 1600 523 0.7
|
|
||||||
SAWTOOTH 9000 1600 523 0.4
|
|
||||||
SINE 9000 1600 523 0.3
|
|
||||||
TRIANGLE 9000 1600 523 0.2
|
|
||||||
Reference in new issue
Block a user