9

Most synthesizers can generate the common standard types of waveforms, such as sine, triangle, square, sawtooth, pulse, noise, etc. And these can be used to create various types of timbre in a synthesized tone.

Today I found myself wondering how to generate specific custom waveforms, such as the one illustrated below:

It's easy to see that this is a pairing of a square wave and a triangle wave with specific differences in amplitude. Of course this would result in a tone sounding different than just a square wave, or just a triangle wave, or any blending of the two. This is a unique waveform with its own unique timbre.

For such a fundamental sort of audio editing, I was surprised that I couldn't think of any way to craft custom tones like this.

Web searches have led me to several solutions that seemed promising at first, but in the end did not allow quite this level of customization.

What are some methods for generating an audio tone from a specific graphed waveform?

Thanks in advance for any help.

  • 2
    You could, you know, just use Python or Matlab to generate the numeric sequence. It's really not that hard to learn those. (or Octave, the free semi-replacement for Matlab) – Carl Witthoft Sep 11 '18 at 15:58
  • @CarlWitthoft What resources might you recommend to steer a Python noob in the right direction to do that? I've never used Python to render audio, although I've been curious about it. – Mentalist Sep 11 '18 at 16:01
  • Hi Mentalist. Unfortunately software recommendations are off topic on this SE. You can try the software recs SE if you can make your question fit its guidelines – Dom Sep 11 '18 at 16:24
  • @Dom Okay, sorry about that. I was on the fence about which SE to post in, actually. Was hoping the audio expertise of this SE would prove more fruitful than the general recommendations SE. Since it is off-topic should I delete this question? – Mentalist Sep 11 '18 at 16:28
  • 1
    I've reopened it now since it fits our scope much better now – Dom Sep 11 '18 at 16:46
8

The zero-skills option is to just freehand-draw the waveform in a dumb GUI editor, even Audacity will do

drawing waveform in Audacity

then copy&paste it a couple of times

DRY

and play that in a loop.

This isn't really practical though: waveform drawing is fiddly and even if the waveform is supposed to be smooth, you'll inevitably leave some rough edges which will actually be very noticeable in the sound (sharp edges = transients = unkempt high overtones). Pretty much anything you can produce this way will sound like a simultaneously boring and nasty buzzsaw.

A simple script in some programming language is much more convenient, and easy enough to do, but frankly it's not that useful either. The ear doesn't really hear waveforms, it rather hears frequency spectra. The standard gang of sine, triangle, sawtooth and square is not just easy to generate with analogue electronics, it's also pretty much all you'll ever need as the starting point for any truely periodic-waveform signal. The fine-tuning of how it should sound is most effectively done with subtractive filtering, rather than with waveform manipulation. You can also hard-bake a frequency response in a wavetable waveform, but this doesn't in principle accomplish anything a sawtooth + FIR filter couldn't also accomplish.

To go beyond the standard analogue oscillator sound, what you need isn't so much finer waveform tweaking as time dependence tweaking. Real instrument signals and most decent synths have not truely periodic waveforms, but distinctive attack sounds and/or modulation effects going on. It can make sense to modulate the waveform itself, – that's what FM / phase-modulation and basically also ring modulator circuits accomplish. Explicitly parameterising the waveform in some other way would be trickier and probably not accomplish much more interesting either.

5

I don't have any particular musical experience with this but some terminology and ideas that might be useful:

  • On the electronics test bench, what you are looking for is known as arbitrary waveform generation and is a feature of signal generators. It totally doesn't make sense to buy one if you're just going to convert its output to digital audio right away, but perhaps knowing the name is useful.

  • If you're up for a bit of programming, you could generate a file containing just the sequence of binary numbers representing the waveform, up to however many repetitions is useful, and then use an audio editor (such as Audacity (which I make no claim is a good choice, just that I know it can do this and it is free)) to convert it to a more typical audio format, if your programming language of choice doesn't have a handy library to write audio files anyway. Just about any language would be able to put the binary data in a file, though some make it easier than others.

    I also have learned that Pure Data, a visual programming environment for music among other things, easily allows doing this; you can literally draw a waveform with your mouse (or load it from a file) as it is playing. (I don't have it in front of me so I can't provide an example; I'll see if I can get to that later.)

  • The technique of using arbitrary waveforms and changing between them to change timbre is called wavetable synthesis. You might have some look finding software intended to do this that also allows loading/creating new waveforms — I don't know whether that's a thing as I only recently learned the concept, but it seemed relevant enough to mention.

  • You could put it together from scratch using simple audio editing operations (I feel like Audacity should be able to do all of this but I don't have it in front of me to check): generate or import a square wave, delete the low half, generate a triangle, then take the whole thing and duplicate/repeat it for as many periods as you need.

  • 1
    Thank you for these suggestions and details. I am familiar with granular synthesis and wavetable synthesis (thanks to Reason's Malström, which invented the hybrid: graintable synthesis). I have never used Pure Data, but just now downloaded it based on your recommendation. I do use Audacity, but in limited scope. In any case I appreciate your answer. – Mentalist Sep 11 '18 at 18:35
2

The way to create this kind of waveform is to combine oscillator sync with amplitude modulation or ring modulation. Here's a similar waveform created by syncing a slightly asymmetrical pulse wave to a triangle wave tuned around 15 semitones higher, and then modulating the amplitude of the triangle wave with the pulse wave.

(I'm using a virtual analog synth I've made in an old version of Reaktor for this; setting the oscillator mix knob around 80 modulates the amplitude of oscillator 2 by 50% of oscillator 2, which means the pulse signal from oscillator 1 varies from 0 to +1.)

Reaktor Juniper

Although this wave may look interesting, it sounds very much like a staightforward square wave, because the two jumps in the waveform have a much greater impact on the sound than the triangle-y bit.

On first-generation samplers like the Casio FZ-1 and the Korg DSS-1, drawing waveforms like these, or combining parts of different waveforms (much like your drawing) was one of the ways to create new sounds. However, on later samplers and synths from these and other manufacturers, this option was rarely present, probably because it's not a very intuitive way to create useful sounds.

In this fragment from a brochure for the Korg DSS-1 you can see the idea of combining the upper half of a sawtooth wave with the lower half of a square wave illustrated, as an example of the sampler's capabilities:

enter image description here

2

Based on this answer, here's a modified Python script to approximate your waveform with pyaudio, scipy and numpy:

import pyaudio
import numpy as np
import matplotlib.pyplot as plt
from scipy import signal

p = pyaudio.PyAudio()

fs = 44100       # sampling rate, Hz, must be integer
duration = 2.0   # in seconds, may be float
f = 440.0        # sine frequency, Hz, may be float

one_cycle = np.linspace(0, 2 * np.pi, int(fs/f), endpoint=False)

waveform = np.concatenate([
    signal.square(one_cycle, duty=1.0) * 0.75 - 0.5, # 3/4 volume and offset
    signal.sawtooth(one_cycle, width=0.5) * 1.0,     # Full volume
    #np.sin(one_cycle)
    ]
)


samples = np.tile(waveform, int(fs * duration / len(waveform)))

print(len(samples))

plt.plot(np.tile(waveform, 2))
plt.show()

# for paFloat32 sample values must be in range [-1.0, 1.0]
stream = p.open(format=pyaudio.paFloat32,
                channels=1,
                rate=fs,
                output=True)

stream.write(samples)

stream.stop_stream()
stream.close()

p.terminate()

The waveform looks like this:

enter preformatted text here

pyaudio plays the tone when the Python script is run.

It's also possible to save it as '.wav' file:

from scipy.io import wavfile
wavfile.write('square_and_triangle.wav', fs, samples)

Here a 2 second example.

  • Downvoter : please explain. – Eric Duminil Sep 12 '18 at 13:05
  • Yeah, seriously, this seems to be a pretty great answer! – Pat Muchmore Sep 12 '18 at 14:52

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.