I am building a pulse wave generator that extends a square wave generator.
My problem is I want the pulse width modulation transition to be smoother I am trying to make the middle sample between the peak and trough of the wave to move smoothly up or down while the width is modulated. This is so higher sounds such as C7 at 2093 Hz don't sound clicky as the duty cycle is modulated.
What needs to be done?
Sample rate is 44100 Bit rate is 16 ampLimit is the highest possible value. duration is the length of waveform in seconds duty cycle is the starting cycle of the waveform
public class PulseGenerator extends SquareGenerator {
// constants
public static final double DEF_MIN_DUTY_CYCLE = 0.05;
public static final double DEF_MAX_DUTY_CYCLE = 0.95;
public static final double DEF_CYCLE_FREQ = 2;
public static final double DEF_HOLD_CYCLE = 0;
// instance variables
double minDutyCycle; // minimum value of duty cycle
double maxDutyCycle; // maximum value of duty cycle
double cycleFreq;
double holdCycle; // if more than zero, the wave will hold the modulation for that period
double dutyCycleRange; // maxDutyCycle - minDutyCycle
boolean setDirection;
// constructor
public PulseGenerator(double amplitude, double frequency, int bitRate,
double duration, double dutyCycle, double minDutyCycle,
double maxDutyCycle, double cycleFreq, double holdCycle) {
super(amplitude, frequency, bitRate, duration, dutyCycle);
// sample data
squareSample = new int[sampleLength];
calculateAmpLimit();
this.dutyCycle = dutyCycle;
waveLength = SAMPLE_RATE / this.frequency;
this.minDutyCycle = minDutyCycle;
this.maxDutyCycle = maxDutyCycle;
this.cycleFreq = cycleFreq * SAMPLE_RATE;
this.holdCycle = holdCycle * SAMPLE_RATE;
dutyCycleRange = this.maxDutyCycle - this.minDutyCycle;
setDirection = false;
}
// one arg cunstructor
public PulseGenerator(double frequency) {
this(AMPLITUDE, frequency, BIT_RATE, DURATION, DEF_DUTY_CYCLE,
DEF_MIN_DUTY_CYCLE, DEF_MAX_DUTY_CYCLE, DEF_CYCLE_FREQ,
DEF_HOLD_CYCLE);
}
// no args constructor
public PulseGenerator() {
this(AMPLITUDE, FREQUENCY, BIT_RATE, DURATION, DEF_DUTY_CYCLE,
DEF_MIN_DUTY_CYCLE, DEF_MAX_DUTY_CYCLE, DEF_CYCLE_FREQ,
DEF_HOLD_CYCLE);
}
// generate waveform method
@Override
public int[] generateWaveForm() {
// define cycle point
int cyclePoint = (int)(cycleFreq / 2 * ((dutyCycle * dutyCycleRange) + minDutyCycle));
// generate the actual waveform
for (int i = 0, j = 0; i < sampleLength; i++, j++) {
double waveCycleRatio = waveLength * dutyCycle;
// same as square generate method
if (j - waveCycleRatio < 0.0) {
finePoint = 1.0;
} else if (j - waveCycleRatio >= 0.0
&& j - waveCycleRatio < 1) {
finePoint = 1 - (j - waveCycleRatio);
} else if (j - waveLength < 0.0) {
finePoint = -1.0;
} else if (j - waveLength >= 0.0) {
finePoint = -1 + (waveLength - j);
}
if (j >= waveLength) {
j = 1;
}
point = (int)finePoint * ampLimit;
squareSample[i] = point;
if (holdCycle > 0) {
holdCycle--;
} else {
dutyCycle = (cyclePoint / (cycleFreq / 2) * dutyCycleRange)
+ minDutyCycle;
if (cyclePoint < cycleFreq / 2 && !setDirection) {
cyclePoint++;
} else if (cyclePoint >= cycleFreq / 2 && !setDirection) {
cyclePoint--;
setDirection = true;
} else if (cyclePoint > 0 && setDirection) {
cyclePoint--;
} else if (cyclePoint <= 0 && setDirection) {
cyclePoint++;
setDirection = false;
}
}
}
// return the sample data
return squareSample;
}
}
I actually found out what was wrong, first I was converting the fine point to an int before I multiplied it by the amp limit. second the code for the fine point needed to be repositioned and multiplied by two.
// generate the actual waveform
for (int i = 0, j = 0; i < sampleLength; i++, j++) {
double waveCycleRatio = waveLength * dutyCycle;
// same as square
if (j - waveCycleRatio < 0.0) {
finePoint = 1.0;
} else if (j - waveCycleRatio >= 0.0
&& j - waveCycleRatio < 1) {
finePoint = 0 - (j - waveCycleRatio - 0.5) * 2;
} else if (j - waveLength < 0.0) {
finePoint = -1.0;
} else if (j - waveLength >= 0.0) {
finePoint = (j - waveLength - 0.5) * 2;
}
if (j >= waveLength) {
j = 1;
}
point = (int)(finePoint * ampLimit);
squareSample[i] = point;
if (holdCycle > 0) {
holdCycle--;
} else {
dutyCycle = (cyclePoint / (cycleFreq / 2) * dutyCycleRange)
+ minDutyCycle;
if (cyclePoint < cycleFreq / 2 && !setDirection) {
cyclePoint++;
} else if (cyclePoint >= cycleFreq / 2 && !setDirection) {
cyclePoint--;
setDirection = true;
} else if (cyclePoint > 0 && setDirection) {
cyclePoint--;
} else if (cyclePoint <= 0 && setDirection) {
cyclePoint++;
setDirection = false;
}
}
}