Trying to convert regular audio in PWM audio

Help for Audacity on Windows.
Forum rules
ImageThis forum is for Audacity on Windows.
Please state which version of Windows you are using,
and the exact three-section version number of Audacity from "Help menu > About Audacity".


Audacity 1.2.x and 1.3.x are obsolete and no longer supported. If you still have those versions, please upgrade at https://www.audacityteam.org/download/.
The old forums for those versions are now closed, but you can still read the archives of the 1.2.x and 1.3.x forums.
8bit_coder
Posts: 32
Joined: Sun Dec 03, 2017 4:31 am
Operating System: Windows 8 or 8.1

Trying to convert regular audio in PWM audio

Post by 8bit_coder » Tue Oct 08, 2019 10:43 pm

Audacity version: 2.2.2

All I'm trying to do is to convert audio from this:
Screenshot (2835).png
Screenshot (2835).png (176.51 KiB) Viewed 1047 times
to this:
Screenshot (2836).png
Screenshot (2836).png (167.46 KiB) Viewed 1047 times
but however much I search online, there's only results about people converting audio to PWM audio just for arduinos, but I want to be able to play back the PWM in audacity

Thanks,
8bit

steve
Site Admin
Posts: 80679
Joined: Sat Dec 01, 2007 11:43 am
Operating System: Linux *buntu

Re: Trying to convert regular audio in PWM audio

Post by steve » Tue Oct 08, 2019 11:23 pm

For pulse width modulation to work, the switching frequency needs to be much higher than the highest waveform frequency that you want to represent. In the case of full-band audio, the switching frequency is often around 1 MHz. A normal sound card can't handle such high frequencies.

If this is just an experiment, then it would be possible convert a very low frequency signal to PWM.

What's the purpose of this?
9/10 questions are answered in the FREQUENTLY ASKED QUESTIONS (FAQ)

8bit_coder
Posts: 32
Joined: Sun Dec 03, 2017 4:31 am
Operating System: Windows 8 or 8.1

Re: Trying to convert regular audio in PWM audio

Post by 8bit_coder » Wed Oct 09, 2019 12:07 am

Pretty much just an experiment. I have sample-rate of the audio track set to 0.5 MHz, so I just wanna mess around and experiment.

DVDdoug
Forum Crew
Posts: 9278
Joined: Fri Sep 10, 2010 11:30 pm
Operating System: Windows 10

Re: Trying to convert regular audio in PWM audio

Post by DVDdoug » Wed Oct 09, 2019 1:30 am

If you've ever programmed you might be able to do something with Nyquist.
I have sample-rate of the audio track set to 0.5 MHz,
Your screenshot says 48000Hz. ;) I don't know if you can play a file with such a sample rate. It's a lot of data so you may not be able to read/process it fast enough, and it's not standard so the drivers may not downsample it to something your soundcard can use.

If you can play it, PWM will probably work because it will be low-pass filtered.

8bit_coder
Posts: 32
Joined: Sun Dec 03, 2017 4:31 am
Operating System: Windows 8 or 8.1

Re: Trying to convert regular audio in PWM audio

Post by 8bit_coder » Wed Oct 09, 2019 2:31 am

The fake "PWM" that I had made by clipping some audio really loudly isn't the sample rate i'll be using, instead I can mess around with a PWM version of audio in slow playback speeds, I think it'll sound interesting

Trebor
Posts: 9847
Joined: Sat Dec 27, 2008 5:22 pm
Operating System: Windows 8 or 8.1

Re: Trying to convert regular audio in PWM audio

Post by Trebor » Wed Oct 09, 2019 7:54 am

A tracker plugin can produce pitch-tracking modulation of a square-wave ...
Attachments
TheDonald , then PWM-ish.wav
(439.82 KiB) Downloaded 31 times

steve
Site Admin
Posts: 80679
Joined: Sat Dec 01, 2007 11:43 am
Operating System: Linux *buntu

Re: Trying to convert regular audio in PWM audio

Post by steve » Wed Oct 09, 2019 2:03 pm

8bit_coder wrote:
Wed Oct 09, 2019 12:07 am
Pretty much just an experiment. I have sample-rate of the audio track set to 0.5 MHz, so I just wanna mess around and experiment.
DVDdoug wrote:
Wed Oct 09, 2019 1:30 am
you might be able to do something with Nyquist.
Yes, you can do this in Nyquist (https://manual.audacityteam.org/man/nyquist.html), and it's pretty simple to do :)

In this example I'll use a sample rate of 100000 Hz (100 kHz), just to make the numbers easier.
I'll also use the "Nyquist Prompt" (https://manual.audacityteam.org/man/nyquist_prompt.html) for running the Nyquist commands.
I shall be using the current "v4" syntax, so the "Use legacy (version 3) syntax" checkbox must be empty (not ticked).

"PWM" is defined on Wikipedia (https://en.wikipedia.org/wiki/Pulse-width_modulation):
"Pulse-width modulation uses a rectangular pulse wave whose pulse width is modulated resulting in the variation of the average value of the waveform"
I shall use this definition, but not their described method of achieving it (because there's a much easier way in Nyquist).

With a sample rate of 100 kHz, we can have a reasonably "square" looking waveform up to about 5 kHz. A 5000 Hz (5 kHz) waveform has a period of 1/5000th second. At a sample rate of 100000, that gives us (100000 * 1/5000) = 20 samples per cycle.
firsttrack000.png
firsttrack000.png (8.18 KiB) Viewed 1010 times

For each cycle of our 5 kHz pulse wave, we need to know what the average amplitude of our signal is. We can then generate a pulse wave where the pulse width is proportional to the average amplitude.

So now we need a test signal to work with.
I've chosen a "chirp" from 1 to 1000 Hz, amplitude 1.0 (start and end), Logarithmic interpolation, duration 5 seconds.
firsttrack001.png
firsttrack001.png (12.78 KiB) Viewed 1010 times

Nyquist has a command for obtaining the average value at specified intervals, called SND-AVG. The result of SND-AVG is a signal in which each sample represents the average value of the signal that we are processing for each time period. We want time periods (steps) of 20 samples, so as to match the frequency of the pulse wave that we will create.

Code: Select all

(snd-avg *track* 20 20 op-average)
*track* is the selected audio that is passed from Audacity to Nyquist
The first number is the number of samples that will be averaged in each step (the "block size")
The second number is the number of samples that we move to get the next block of samples.
op-average tells the command to get the mean average of the samples in each block.

Thus the result will be a signal in which the first sample is equal to the average of the first 20 samples of the test tone, the second sample is the average of the next 20 samples, and so on.

We can assign the result to a variable using the SETF command:

Code: Select all

(setf bias (snd-avg *track* 20 20 op-average))
We have set the value of "bias" to the result of (snd-avg *track* 20 20 op-average)

Now we can create our pulse wave using the Nyquist function OSC-PULSE
Remembering that our required frequency is 5000 Hz, the pulse generator command will be:

Code: Select all

(osc-pulse 5000 bias)
where "bias" is the control signal that we created from SND-AVG.

So here's the full code:

Code: Select all

(setf bias (snd-avg *track* 20 20 op-average))
(osc-pulse 5000 bias)
and here's the result:
firsttrack002.png
firsttrack002.png (7.78 KiB) Viewed 1010 times
but let's zoom in a bit at around the 4.0 second mark:
firsttrack003.png
firsttrack003.png (8.73 KiB) Viewed 1010 times


Observations:

1)
If you zoom in on the PWM waveform near the start of the track, you will see that the pulse wave is not always complete.
firsttrack005.png
firsttrack005.png (8.22 KiB) Viewed 1010 times
This is because the width of the pulse is approaching zero, but we only have samples every 1/100000th second.

2)
At the high frequency end of the "chirp", the PWM signal only has about 5 pulses to define the waveform, so we can see that a 1000 Hz signal is approaching the limit of what a 5000 Hz pulse can handle.
tracks000.png
tracks000.png (19.42 KiB) Viewed 1010 times


Decoding:

If you try playing the PWM signal (turn the volume down a bit, it will be very loud), you will notice that it does not sound much like our original "chirp". To recover our chirp sound, we need to decode the PWM signal. This is very easy to do. Just apply a low pass filter to remove the 5000 Hz carrier, and the result will be the "averaged" value of the pulses:

Code: Select all

(lowpass8 *track* 1500)
LOWPASS8 is a steep lowpass filter.
The filter frequency of 1500 Hz is chosen to be far enough above our maximum signal frequency (1000 Hz) to not attenuate the signal too much, but well below our 5 kHz modulation frequency.

Note that at the low frequency end of the chirp, the demodulated output has distinct steps. This is because the 5000 Hz pulse wave has only 20 samples per cycle, so only 20 possible sizes for rectangular pulses, and because the change from one size rectangle to the next is so slow, our simple demodulator is unable to smooth out the steps.

Overall we can see that with a 100 kHz sample rate, and a 5 kHz pulse, our available audio bandwidth is around 200 Hz to 1000 Hz.
9/10 questions are answered in the FREQUENTLY ASKED QUESTIONS (FAQ)

8bit_coder
Posts: 32
Joined: Sun Dec 03, 2017 4:31 am
Operating System: Windows 8 or 8.1

Re: Trying to convert regular audio in PWM audio

Post by 8bit_coder » Wed Oct 09, 2019 3:43 pm

Wow, thanks! I was writing something in nyquist myself but it was giving me a completely wrong output, this makes much more sense!

steve
Site Admin
Posts: 80679
Joined: Sat Dec 01, 2007 11:43 am
Operating System: Linux *buntu

Re: Trying to convert regular audio in PWM audio

Post by steve » Wed Oct 09, 2019 4:07 pm

Enjoy your experiments :)
9/10 questions are answered in the FREQUENTLY ASKED QUESTIONS (FAQ)

8bit_coder
Posts: 32
Joined: Sun Dec 03, 2017 4:31 am
Operating System: Windows 8 or 8.1

Re: Trying to convert regular audio in PWM audio

Post by 8bit_coder » Wed Oct 09, 2019 4:53 pm

Hmmm, for some reason, putting the code in the nyquist prompt box results in this strange error output:

Code: Select all

error: bad argument type - #(#<Sound: #5ace328> #<Sound: #5ace568>)
Function: #<Subr-SND-AVG: #aa4b810>
Arguments:
  #(#<Sound: #5ace328> #<Sound: #5ace568>)
  20
  20
  1
Function: #<FSubr-SETF: #aa46a30>
Arguments:
  BIAS
  (SND-AVG *TRACK* 20 20 OP-AVERAGE)
1> error: unbound variable - BIAS
if continued: try evaluating symbol again
Function: #<Subr-SND-AVG: #aa4b810>
Arguments:
  #(#<Sound: #5ace328> #<Sound: #5ace568>)
  20
  20
  1
Function: #<FSubr-SETF: #aa46a30>
Arguments:
  BIAS
  (SND-AVG *TRACK* 20 20 OP-AVERAGE)
2> 1> 
From what I can tell, it doesn't think track is a valid arguement, and it's interpreting bias as a variable.

Post Reply