Create a gist now

Instantly share code, notes, and snippets.

Generate waveform images from audio files
# Requires pydub (with ffmpeg) and Pillow
# Usage: python <audio_file>
import sys
from pydub import AudioSegment
from PIL import Image, ImageDraw
class Waveform(object):
bar_count = 107
db_ceiling = 60
def __init__(self, filename):
self.filename = filename
audio_file = AudioSegment.from_file(
self.filename, self.filename.split('.')[-1])
self.peaks = self._calculate_peaks(audio_file)
def _calculate_peaks(self, audio_file):
""" Returns a list of audio level peaks """
chunk_length = len(audio_file) / self.bar_count
loudness_of_chunks = [
audio_file[i * chunk_length: (i + 1) * chunk_length].rms
for i in range(self.bar_count)]
max_rms = max(loudness_of_chunks) * 1.00
return [int((loudness / max_rms) * self.db_ceiling)
for loudness in loudness_of_chunks]
def _get_bar_image(self, size, fill):
""" Returns an image of a bar. """
width, height = size
bar ='RGBA', size, fill)
end ='RGBA', (width, 2), fill)
draw = ImageDraw.Draw(end)
draw.point([(0, 0), (3, 0)], fill='#c1c1c1')
draw.point([(0, 1), (3, 1), (1, 0), (2, 0)], fill='#555555')
bar.paste(end, (0, 0))
bar.paste(end.rotate(180), (0, height - 2))
return bar
def _generate_waveform_image(self):
""" Returns the full waveform image """
im ='RGB', (840, 128), '#f5f5f5')
for index, value in enumerate(self.peaks, start=0):
column = index * 8 + 2
upper_endpoint = 64 - value
im.paste(self._get_bar_image((4, value * 2), '#424242'),
(column, upper_endpoint))
return im
def save(self):
""" Save the waveform as an image """
png_filename = self.filename.replace(
self.filename.split('.')[-1], 'png')
with open(png_filename, 'wb') as imfile:
self._generate_waveform_image().save(imfile, 'PNG')
if __name__ == '__main__':
filename = sys.argv[1]
waveform = Waveform(filename)
mgrady3 commented Oct 30, 2015

one small suggestion / observation

The class waveform defines only two instance variables in its init() method:

self.filename and self.peaks

however, as far as I can see, self.filename is never explicitly used. Instead, the filename is parsed from command line in main, passed to Waveform.init and then always just referenced as filename rather than the actual instance variable, self.filename.

I would suggest changing and waveform.init() to explicitly use the instance variable, otherwise the first line of the init function is essentially useless unless there was a plan to extend the class in the future with additional functionality

mixxorz commented Oct 30, 2015

You're right, I didn't notice that. Updated.


You might want to change line 67 to with open(png_filename, 'wb') as imfile:

This will make the script run on Python3 as well! 👍

mixxorz commented Nov 4, 2015

Cool thanks! Updated.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment