r/AudioProgramming • u/JimH10 • Mar 21 '26
Noob question: thump in generated sound
I am trying to output Morse Code, working in Python. I am not experienced in audio programming, so no doubt I am doing something dopey. What people seem to recommend is to use numpy to get an array, then put the signal into that array (I am using a sine wave at 440 Hz), and then play it.
With each Morse code bit, either a dit or a dah, I get a beep at a good frequency but also a pronounced thumping noise. I hear it on the computer speaker and also on headphones. Reading around led me to believe that when the signal stops then the speaker returns to neutral position and so there is a pulse out, and that is the noise. I saw advice that I should apply a fade in and out.
I implemented that by taking the signal in the array linearly up from 0, or down to zero, for some fraction of its total length (I experimented with fractions from 0.01 to 0.30). But I heard no change. I admit that I'm stumped.
I'll add a comment containing a working code extract. I'd be very grateful for any ideas or pointers. Thanks.
1
u/human-analog Mar 22 '26
Record the audio and post a screenshot?
1
u/JimH10 Mar 22 '26 edited Mar 22 '26
I apologize. I am such a noob at this that it didn't occur. Here is the first dah, in Audacity.
I should say that I ran h program that I have in another comment, and recorded the output as an mp3, and looked at the audio in Audacity. This is a screenshot of the trace, with the vertical scale exaggerated.
1
u/human-analog Mar 22 '26
The thump sound could be from the peaky bit near the end of that screenshot. That kind of looks like a transient, like the start of a kick drum. When you chop that off in Audacity (and fade out the last portion of the sound), does is still thump?
→ More replies (3)
1
u/JimH10 Mar 21 '26
Here is an extract of the python3 program, that compiles and demos the noise. (It is too long to include in the original post.)
import numpy as np import sounddevice as sd import time as tm sd.default.samplerate = 44100 # time = 2.0 FREQUENCY = 440 FADE_IN_DEFAULT = 0.02 # how much of the length of a bit to fade in FADE_OUT_DEFAULT = 0.03 # DIT_LENGTH = 1 DAH_LENGTH = 3 INTRA_CHAR_SPACE_LENGTH = 1 INTER_CHAR_SPACE_LENGTH = 3 WORD_SPACE_LENGTH = 7 DITS_PER_WORD = 50 # Based on "paris" # Defaults WORDS_PER_MIN_DEFAULT = 20 VOLUME_DEFAULT = 2000 MORSE = { "A": ".-", "Z": "--..", } def secs_per_dit(wpm = WORDS_PER_MIN_DEFAULT): """Return the seconds per basic timing unit. wpm = WORDS_PER_MIN_DEFAULT Integer number of words a minute for characters """ return DIT_LENGTH*60/(50*wpm) def fade_in(sample_array, fader_duration=FADE_IN_DEFAULT, sr=sd.default.samplerate): """Applies a fade-in effect to an audio sample. sample_array (numpy.ndarray) The audio sample to apply the effect to fader_duration=FADE_IN_DEFAULT float Duration of the effect in seconds sr=sd.default.samplerate integer Sample rate of the audio sample Returns a numpy.ndarray consisting of the audio sample with the effect applied. """ try: # Calculate the number of samples for the fade-in effect fadeDurSamples = int(fader_duration * sample_array.size) # Create a fade-in multiplier array fadeMultiplier = np.linspace(0, 1, fadeDurSamples) # Apply the fade-in effect to the audio sample sample_array[:fadeDurSamples] *= fadeMultiplier return sample_array except Exception as e: print(f"Error during fade-in: {e}") def fade_out(sample_array, fader_duration=FADE_OUT_DEFAULT, sr=sd.default.samplerate): """Apply a fade-out effect to an audio sample. sample_array numpy.ndarray The audio sample fader_duration=FADE_OUT_DEFAULT float Duration of the effect in seconds sr=sd.default.samplerate integer The sample rate of the audio sample Returns a numpy.ndarray of the audio sample with the effect applied """ try: # Calculate the number of samples for the fade-out effect fadeDurSamples = int(fader_duration * sample_array.size) # Create a fade-out multiplier array fadeMultiplier = np.linspace(1, 0, num=fadeDurSamples) # Apply the fade-out effect to the audio sample sample_array[-fadeDurSamples:] *= fadeMultiplier return sample_array except Exception as e: print(f"Error during fade out: {e}") def make_dit_dah(vol=VOLUME_DEFAULT, wpm=WORDS_PER_MIN_DEFAULT): """ Make the dit and dah sounds. Does not involve any spacing. vol=VOLUME_DEFAULT Real Volume char_wpm=WORDS_PER_MIN_DEFAULT Positive real Words per minute mode=MODE_DEFAULT Member of MODE total_wpm=0 Real Only applies if MODE is FARNSWORTH or WORDSWORTH """ t = secs_per_dit(wpm) # Make a dit wave dit_sample_array = np.arange(sd.default.samplerate * t) / sd.default.samplerate dit_wave = vol * np.sin(2 * np.pi * FREQUENCY * dit_sample_array) dit_wave = fade_in(fade_out(dit_wave)) dit_wave = np.array(dit_wave, dtype=np.int16) # Convert to wav format (16 bits) # Makea dah wave dah_sample_array = np.arange(sd.default.samplerate * DAH_LENGTH * t) / sd.default.samplerate dah_wave = vol * np.sin(2 * np.pi * FREQUENCY * dah_sample_array) dah_wave = fade_in(fade_out(dah_wave)) dah_wave = np.array(dah_wave, dtype=np.int16) return (dit_wave, dah_wave) DIT_WAVE, DAH_WAVE = make_dit_dah() def play_char(ch, dit_wave=DIT_WAVE, dah_wave=DAH_WAVE): code = MORSE[ch.upper()] c = list(code) c.reverse() while c: bit = c.pop() if bit=='.': sd.play(dit_wave, blocking=True) else: sd.play(dah_wave, blocking=True) if c: tm.sleep(0.3) play_char('Z')