My holiday may be looming, but actually, after seeing the relatively positive response to my last quick project which looked at abusing a Rohde & Schwarz RTM3004’s arbitrary waveform generation features to produce audio, I thought it might be a good idea to up the ante somewhat and go even more bonkers and try to make music with a power supply. It'sa me, part two!
Sound but with a Catch (or Two)!
This time, the challenge gets tougher. Arbitrary waveform generators are designed primarily to produce fast waveforms, often much faster than necessary for audible sound. Lab benchtop DC power supplies, on the other hand, generally are not designed to produce much of a waveform at all, let alone be fast. Instead, they’re designed to generate a steady output, changing at a much slower rate when reprogrammed to a different output. Even though they may advertise sequencing or “arbitrary” waveform capabilities, these are much more limited by comparison. Another consideration is that the supplies are often not capable of producing negative voltages, unless they are a four-quadrant variety.
This is where I have lucked out – I have one of Rohde & Schwarz’s specialty power supplies, the NGM202, which is fast. Whereas their performance series HMP4040.04 can run 100-points per second, the NGM202 can do a blistering 1000-points per second. With fast transient response features and a fast recovery time, challenging down-programming transitions (i.e. high to low voltage) should be handled well as the power supply is a two-quadrant unit. Let’s put that to the test.
Why is this important? Well recall that sound waves can be made from alternating electrical waves. The problem with a slow arbitrary waveform generator that can run 100Hz is that the maximum frequency of sound you can get (assuming a sample with full output followed by a sample with zero output for a square wave) is 50Hz. That’s a power-line hum. With a waveform generator that can do 1000Hz, this increases to 500Hz or about B4 on the musical scale. That’s not the greatest, but at least that’s something we can work with.
Another issue is timing granularity. With arbitrary waveform on power supplies, it is normally specified as a quad consisting of voltage, current, dwell time and interpolation. The dwell time units in the case of the NGM202 seem to be 1ms. Because of this, the actual tones that the unit can generate are quite limited at the high end – the scale goes something like this:
Dwell (ms) Frequency (Hz) 1 500 2 250 3 166.6666667 4 125 5 100 6 83.33333333 7 71.42857143 8 62.5 9 55.55555556 10 50 11 45.45454545 12 41.66666667 13 38.46153846 14 35.71428571 15 33.33333333 16 31.25 17 29.41176471 18 27.77777778 19 26.31578947 20 25 21 23.80952381 22 22.72727273 23 21.73913043 24 20.83333333 25 20 26 19.23076923 27 18.51851852 28 17.85714286 29 17.24137931 30 16.66666667 31 16.12903226 32 15.625 33 15.15151515 34 14.70588235 35 14.28571429 36 13.88888889 37 13.51351351 38 13.15789474 39 12.82051282 40 12.5
That’s not quite the musical scale, but I’ll give it a shot anyway. The other option would be to try and synthesize something akin to a pulse wave (e.g. 1ms on, x ms off) to get some in-between frequency values. Another simpler option would be to use the lower end of the musical scale, as the alignments are a bit finer with lower frequencies – this is the option I decided to choose.
Another issue is that the power supply is not actually outputting a true AC signal, instead, it will be alternating between zero and a positive DC voltage. This results in an average voltage across the speaker which is non-zero, potentially resulting in extra heating of the speaker coil and asymmetric cone excursion. If the amplitude is limited, this isn’t a big issue, but the easy fix for this is to shove a capacitor in series to block the DC (although with frequency-response altering effects). For simplicity, I decided to forego this entirely.
There is one upside to using a power supply though – you can produce really loud sounds. In fact, you could even blow up a speaker if you give it enough juice … so best be conservative with the settings.
Making the Idea Happen
The first thing I needed was some music to play. Video-game music usually fits well with square-waves, so I decided that after my joke with the oscilloscope project, that I should just do the Mario theme. Being lazy and not wanting to code it all myself, I stole some Arduino code from https://gist.github.com/gskielian/6135641.
But since I like to use pyvisa for automation, I would have to translate the code into Python. Or so I thought. What I discovered is that if I just reimplement the tone() and delay() functions to make it compatible with the NGM202, then the rest of the code can remain unchanged. Python is even friendly enough to ignore the semi-colons at the end of the Arduino lines … for illustration, the code is below without any cleaning up:
# Mario Theme on a R&S NGM202 Power Supply # by Gough Lui (goughlui.com) - March 2020 # # Adapted from # https://gist.github.com/gskielian/6135641 # # No warranties. Code depends on pyvisa. Change SCPI resource identifier to match your device. # No liability accepted for damages however incurred. import visa import time def tone (c,f,t) : dura = "{:4e}".format(8/f) # Scale down frequency by 4, half period, scientific format string ins_ngm202.write("ARB:DATA "+str(outvolt)+","+str(outcur)+","+dura+",0,0.0,"+str(outcur)+","+dura+",0") ins_ngm202.write("ARB:TRAN "+str(outch)) ins_ngm202.write("ARB 1") ins_ngm202.write("OUTP 1") ins_ngm202.query("*OPC?") time.sleep(t/1000*2) # Play notes twice as long ... ins_ngm202.write("OUTP 0") def delay (t) : time.sleep(t/1000) # Keep silent delays the same though ... resource_manager = visa.ResourceManager() ins_ngm202 = resource_manager.open_resource("TCPIP0::192.168.80.18::INST0::INSTR") ins_ngm202.timeout = 10000 outvolt = 0.75 outcur = 0.5 outch = 1 print("Available:" + "\n" + ins_ngm202.query("*IDN?")) input("Play Mario Theme?") # Set Up NGM202 print("Setting Up - NGM202") ins_ngm202.write("INST:NSEL "+str(outch)) ins_ngm202.write("SENS:VOLT:RANG:AUTO 0") ins_ngm202.write("SENS:VOLT:RANG 5") ins_ngm202.write("SENS:CURR:RANG:AUTO 0") ins_ngm202.write("SENS:CURR:RANG 1") ins_ngm202.write("OUTP 0") ins_ngm202.write("OUTP:GEN 0") ins_ngm202.write("OUTP:MODE SOUR") ins_ngm202.write("SOUR:VOLT 0.0") ins_ngm202.write("SOUR:CURR "+str(outcur)) ins_ngm202.query("*OPC?") # Begin Playback - Code is Arduino Code Directly Taken! # Lucky Python ignores all semi-colons and I have written tone() and delay() # functions to take care of the code without needing modification. print("Begin Melody") tone(9,660,100); delay(150); tone(9,660,100); delay(300); tone(9,660,100); delay(300); tone(9,510,100); delay(100); tone(9,660,100); delay(300); tone(9,770,100); delay(550); tone(9,380,100); delay(575); tone(9,510,100); delay(450); tone(9,380,100); delay(400); tone(9,320,100); delay(500); tone(9,440,100); delay(300); tone(9,480,80); delay(330); tone(9,450,100); delay(150); tone(9,430,100); delay(300); tone(9,380,100); delay(200); tone(9,660,80); delay(200); tone(9,760,50); delay(150); tone(9,860,100); delay(300); tone(9,700,80); delay(150); tone(9,760,50); delay(350); tone(9,660,80); delay(300); tone(9,520,80); delay(150); tone(9,580,80); delay(150); tone(9,480,80); delay(500); tone(9,510,100); delay(450); tone(9,380,100); delay(400); tone(9,320,100); delay(500); tone(9,440,100); delay(300); tone(9,480,80); delay(330); tone(9,450,100); delay(150); tone(9,430,100); delay(300); tone(9,380,100); delay(200); tone(9,660,80); delay(200); tone(9,760,50); delay(150); tone(9,860,100); delay(300); tone(9,700,80); delay(150); tone(9,760,50); delay(350); tone(9,660,80); delay(300); tone(9,520,80); delay(150); tone(9,580,80); delay(150); tone(9,480,80); delay(500); tone(9,500,100); delay(300); tone(9,760,100); delay(100); tone(9,720,100); delay(150); tone(9,680,100); delay(150); tone(9,620,150); delay(300); tone(9,650,150); delay(300); tone(9,380,100); delay(150); tone(9,430,100); delay(150); tone(9,500,100); delay(300); tone(9,430,100); delay(150); tone(9,500,100); delay(100); tone(9,570,100); delay(220); tone(9,500,100); delay(300); tone(9,760,100); delay(100); tone(9,720,100); delay(150); tone(9,680,100); delay(150); tone(9,620,150); delay(300); tone(9,650,200); delay(300); tone(9,1020,80); delay(300); tone(9,1020,80); delay(150); tone(9,1020,80); delay(300); tone(9,380,100); delay(300); tone(9,500,100); delay(300); tone(9,760,100); delay(100); tone(9,720,100); delay(150); tone(9,680,100); delay(150); tone(9,620,150); delay(300); tone(9,650,150); delay(300); tone(9,380,100); delay(150); tone(9,430,100); delay(150); tone(9,500,100); delay(300); tone(9,430,100); delay(150); tone(9,500,100); delay(100); tone(9,570,100); delay(420); tone(9,585,100); delay(450); tone(9,550,100); delay(420); tone(9,500,100); delay(360); tone(9,380,100); delay(300); tone(9,500,100); delay(300); tone(9,500,100); delay(150); tone(9,500,100); delay(300); tone(9,500,100); delay(300); tone(9,760,100); delay(100); tone(9,720,100); delay(150); tone(9,680,100); delay(150); tone(9,620,150); delay(300); tone(9,650,150); delay(300); tone(9,380,100); delay(150); tone(9,430,100); delay(150); tone(9,500,100); delay(300); tone(9,430,100); delay(150); tone(9,500,100); delay(100); tone(9,570,100); delay(220); tone(9,500,100); delay(300); tone(9,760,100); delay(100); tone(9,720,100); delay(150); tone(9,680,100); delay(150); tone(9,620,150); delay(300); tone(9,650,200); delay(300); tone(9,1020,80); delay(300); tone(9,1020,80); delay(150); tone(9,1020,80); delay(300); tone(9,380,100); delay(300); tone(9,500,100); delay(300); tone(9,760,100); delay(100); tone(9,720,100); delay(150); tone(9,680,100); delay(150); tone(9,620,150); delay(300); tone(9,650,150); delay(300); tone(9,380,100); delay(150); tone(9,430,100); delay(150); tone(9,500,100); delay(300); tone(9,430,100); delay(150); tone(9,500,100); delay(100); tone(9,570,100); delay(420); tone(9,585,100); delay(450); tone(9,550,100); delay(420); tone(9,500,100); delay(360); tone(9,380,100); delay(300); tone(9,500,100); delay(300); tone(9,500,100); delay(150); tone(9,500,100); delay(300); tone(9,500,60); delay(150); tone(9,500,80); delay(300); tone(9,500,60); delay(350); tone(9,500,80); delay(150); tone(9,580,80); delay(350); tone(9,660,80); delay(150); tone(9,500,80); delay(300); tone(9,430,80); delay(150); tone(9,380,80); delay(600); tone(9,500,60); delay(150); tone(9,500,80); delay(300); tone(9,500,60); delay(350); tone(9,500,80); delay(150); tone(9,580,80); delay(150); tone(9,660,80); delay(550); tone(9,870,80); delay(325); tone(9,760,80); delay(600); tone(9,500,60); delay(150); tone(9,500,80); delay(300); tone(9,500,60); delay(350); tone(9,500,80); delay(150); tone(9,580,80); delay(350); tone(9,660,80); delay(150); tone(9,500,80); delay(300); tone(9,430,80); delay(150); tone(9,380,80); delay(600); tone(9,660,100); delay(150); tone(9,660,100); delay(300); tone(9,660,100); delay(300); tone(9,510,100); delay(100); tone(9,660,100); delay(300); tone(9,770,100); delay(550); tone(9,380,100); delay(575); print("Song End. Closing instrument!") ins_ngm202.write("OUTP 0") ins_ngm202.write("OUTP:GEN 0") ins_ngm202.write("ARB 0") ins_ngm202.close()
Disclaimer: I do not recommend running this code on your own unit – if you do so, you do it at your own risk. This code is particularly harsh on the NGM202’s output relay, toggling at each note due to how the NGM202 handles Arbitrary Waveform on/off events. It also causes the output to be cycled very quickly, which results in a high stress on the power supply’s regulation circuitry. But it does work, for the most part, as you will see in the next part.
The code allows you to define the instrument’s VISA resource name, the output amplitude, current limit and channel. This probably won’t work on any supply which doesn’t have at least the NGL/NGM’s capabilities and syntax. Some of the hacks include a transposition done within the tone function and tempo slowdown as the power supply gets really unhappy if you command it to change its output too quickly.
The speaker is one from a computer peripherals-brand which I picked up at a liquidation sale and shoved in my junk-box for many years. It is connected directly to the Channel 1 outputs using a pair of banana to crocodile clip cables.
Video
The result is above – because of time constraints, I didn’t bother to create much of an explanatory introduction, as … well, we have this article and I’m rapidly running out of time. But that’s not to say I don’t have more ideas that I want to try. The result isn’t exactly as neat as I thought it would be – some of the lost notes appear to be a firmware issue with the NGM202 when the arbitrary function is called upon a lot in a short space of time. In fact, I found that if the code used “ARB 0” to stop the arbitrary function rather than “OUTP 0”, it would play only a third or half the song before getting stuck and staying silent. But that’s perhaps not unexpected, since I am pushing the capabilities right to the edge. I bet Rohde & Schwarz didn’t intend their power supply to be used like this …
Conclusion
The conclusion is obvious – power supplies can create music too. But not all power supplies. You need one that’s fast in output programming capabilities with a fast arbitrary waveform generation function as well. But if it’s fast enough … it can kind of make music and quite loudly too. It’s not an intended use for power supplies, hence discovering a few issues, and is harsh on the regulation circuit and output relay. I don’t recommend running this code on your own unit. But I hope you might have enjoyed this follow-up to my abuse of test equipment for weird acoustical profit.
Top Comments