Mimic PC Speaker Output (not Adlib or Sound Blaster)
Posted: Mon Jun 06, 2022 10:37 am
Hello,
I was encouraged to post here by Peter Hinch.
I would like to make a library which plays all sorts of PC speakers sounds from DOS games (not Adlib or Sound Blaster), starting with Apogee Software's Wolfenstein 3d. Peter said, "The way I would approach this is to use the PIO to emulate the original Assembler code in your link. Using the FIFO would enable a nonblocking driver."
I have written a small uasyncio code to output a series of frequencies using PWM/duty_u16. I wrote about it here:
https://github.com/gurgleapps/musicode/issues/1
Example video: https://www.youtube.com/watch?v=5v36e4_ ... Zdenda1990
Shareware link: https://archive.org/details/Wolfenstein3d
I tried decoding the audio format, but I don't seem to be able to get the frequencies correct. My code seems to play notes in the right sequence. https://moddingwiki.shikadi.net/wiki/AudioT_Format
I ended up resorting to using a tool called keenwave to playback the sounds for reference. Using the .dat output from keenwave, I confirmed that my Python code did extract the sound chunks correctly. There's a link in this thread to download an old version: http://keenmodding.org/viewtopic.php?t=6223
I'm not sure if this is a problem with the cheap speaker I'm using, if I converted the values incorrectly (LSB invert the values... how?), or if something wrong my with timing. I also couldn't get it running using the Python winsound library. 140 Hz should be ~7 ms, but it isn't right. I also tried using Audacity to examine a waveform, but that was not helpful.
# Hex values for sound 36 in Wolfenstein 3D... (I'm not sure where this stands on copyright law)
# 95 00 00 00 46 02 45 42 40 3F 3E 3D 3C 3A 39 38 36 34 33 32 31 2F 2E 2D 2C 2C 2D 2F 31 32 34 36 3A 3B 3C 3B 39 37 34 32 2E 2A 28 27 26 25 24 24 23 23 25 26 28 29 2B 2C 2C 2B 2A 28 22 20 1F 1E 1D 1B 1A 1A 1A 19 18 18 18 18 18 18 18 18 18 18 18 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 13 13 13 13 13 13 00 00 00 13 13 13 13 13 13 13 13 00 00 00 13 13 13 13 13 13 00 00 00 00 00 10 10 10 10 10 10 10 10 10 10 10 00 00 00 10 10 10 10 10 10 10 10 10 00
Here's the song using gurgleapp's non-async version, since this doesn't have notes assigned to the frequencies
Now that I think about it, I could possibly use Keenwave to manually create my own sound, using the interface to assign maximum and minimum values and compare their hexadecimal values. Maybe I can use a tuning app on my smartphone to determine the frequency to see if the problem is with my equipment (and I really need an amplifier and a better speaker).
I was encouraged to post here by Peter Hinch.
I would like to make a library which plays all sorts of PC speakers sounds from DOS games (not Adlib or Sound Blaster), starting with Apogee Software's Wolfenstein 3d. Peter said, "The way I would approach this is to use the PIO to emulate the original Assembler code in your link. Using the FIFO would enable a nonblocking driver."
I have written a small uasyncio code to output a series of frequencies using PWM/duty_u16. I wrote about it here:
https://github.com/gurgleapps/musicode/issues/1
Example video: https://www.youtube.com/watch?v=5v36e4_ ... Zdenda1990
Shareware link: https://archive.org/details/Wolfenstein3d
I tried decoding the audio format, but I don't seem to be able to get the frequencies correct. My code seems to play notes in the right sequence. https://moddingwiki.shikadi.net/wiki/AudioT_Format
I ended up resorting to using a tool called keenwave to playback the sounds for reference. Using the .dat output from keenwave, I confirmed that my Python code did extract the sound chunks correctly. There's a link in this thread to download an old version: http://keenmodding.org/viewtopic.php?t=6223
I'm not sure if this is a problem with the cheap speaker I'm using, if I converted the values incorrectly (LSB invert the values... how?), or if something wrong my with timing. I also couldn't get it running using the Python winsound library. 140 Hz should be ~7 ms, but it isn't right. I also tried using Audacity to examine a waveform, but that was not helpful.
# Hex values for sound 36 in Wolfenstein 3D... (I'm not sure where this stands on copyright law)
# 95 00 00 00 46 02 45 42 40 3F 3E 3D 3C 3A 39 38 36 34 33 32 31 2F 2E 2D 2C 2C 2D 2F 31 32 34 36 3A 3B 3C 3B 39 37 34 32 2E 2A 28 27 26 25 24 24 23 23 25 26 28 29 2B 2C 2C 2B 2A 28 22 20 1F 1E 1D 1B 1A 1A 1A 19 18 18 18 18 18 18 18 18 18 18 18 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 13 13 13 13 13 13 00 00 00 13 13 13 13 13 13 13 13 00 00 00 13 13 13 13 13 13 00 00 00 00 00 10 10 10 10 10 10 10 10 10 10 10 00 00 00 10 10 10 10 10 10 10 10 10 00
Here's the song using gurgleapp's non-async version, since this doesn't have notes assigned to the frequencies
Code: Select all
tune = [0, 0, 0, 4200, 120, 4800, 4680, 4560, 4500, 4320, 4200, 4080, 3960, 3840, 3720, 3600, 3480, 3420, 3360, 3300, 3240, 3240, 3240, 3240, 3360, 3480, 3540, 3600, 3720, 3780, 3840, 3900, 3900, 3900, 3840, 3840, 3840, 3720, 3600, 3540, 3480, 3420, 3360, 3300, 3240, 3180, 3060, 3000, 2940, 2820, 2760, 2700, 2640, 2640, 2580, 2580, 2580, 2580, 2580, 2580, 2580, 2640, 2880, 2940, 3060, 3060, 3120, 3060, 3060, 3000, 2940, 2880, 2820, 2640, 2520, 2400, 2280, 2160, 2040, 1920, 1860, 1800, 1800, 1740, 1740, 1680, 1680, 1680, 1740, 1800, 1920, 1980, 2040, 2100, 2100, 2160, 2100, 2040, 1920, 1860, 1740, 1620, 1500, 1440, 1260, 1140, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1080, 1080, 1080, 1080, 1080, 1080, 1080, 1080, 0, 0, 0, 1080, 1080, 1080, 1080, 1080, 1080, 1080, 0, 0, 1080, 1080, 1080, 1080, 1080, 1080, 1080, 0, 0, 0, 0, 0, 0, 0, 0, 960, 960, 960, 960, 960, 960, 960, 960, 960, 0, 0, 0, 960, 960, 960, 960, 960, 960, 960, 960, 960, 0, 0, 0, 960, 960, 960, 960, 960, 960, 960, 960, 1020, 1020, 0, 0, 0, 0, 0, 0, 900, 900, 900, 900, 900, 900, 900, 900, 900, 0, 0, 0, 900, 900, 900, 900, 900, 900, 900, 900, 0, 0]
speaker = machine.PWM(machine.Pin(8))
def play_note(note):
if note == 0:
speaker.duty_u16(0)
else:
speaker.duty_u16(int(65535/2))
speaker.freq(note)
time.sleep(.0079)
for note in tune:
play_note(note)
speaker.duty_u16(0)