Skip to main content

Converts graphs to sound.

Project description

graph2sound

This is a Python library for adding some auditory feedback to your graphs. Alternatively, this library has capabilities such as creating smooth variable-frequency waveforms or playing simple MIDI-tones.

Installation

Use pip install graph2sound to install the module.
Alternatively, download graph2sound.py and save it in your working directory.

Dependencies

numpy for array creation and modification.
scipy for signal modification and interpolation.
sounddevice is required for playback. However, as the wav + samplerate can be extracted with graph2wav, it is not strictly needed.

Usage

Depending on what you want to do, g2s may be called as a single function. These functions acutally chain some subroutines together to assemble and play the final tone.
The general syntax is as follows:

import graph2sound as g2s
g2s.graph2sound(y[,x],[t=,r=,s=,l=,h=,w=,f=])

Demo

Test g2s by g2s.graph2sound(). Plays a demo sound.

Single y series

At the very least, g2s requires a single data series consisting of two points or more.

y = [i**2 for i in range(10)]
g2s.graph2sound(y)

This will play a tone which increases in frequency quadratically. Every point in the series is played for an equal amount of time (linear time mapping).

Single x y series

If you have an associated time series, for example a result from an ODE solver, you may also map your data points to time. Note: the lengths of the two series must be equal.

import numpy as np
t = np.logspace(0.1,1,100)
y = np.sin(t)
g2s.graph2sound(y,t)

This will play a sound which steadily increases in frequency, as apposed to a logarithmic increase in frequency.

Two-dimensional y series

If you have a series of y data which is 2D, g2s will automatically convert all rows to wavs and adds them.

y = [[i*j for i in range(5)] for j in range(5)]
g2s.graph2sound(y)

This will play the normalized summed waveforms, row major.

Keyword use and more examples

import pandas as pd
d = {'x':[0,0.1,0.2,3],'y':[0,0,0,1]}
df = pd.DataFrame(d)
g2s.graph2sound(df['y'],df['x'],t=1.5,l=440,h=880,w='sawtooth')

Plays from a dataframe. Has a 1.5 second duration, the lowest/highest tones are 440 and 880 and the synth uses a sawtooth wave.

from scipy import signal
import numpy as np
x = np.linspace(0,20)
y = signal.square(x)
g2s.graph2sound(y,x,w='square')

This is what a square square wave sounds like.

Keywords

g2s.graph2sound takes multiple optional keywords:
t = float,int Default: 2 seconds. Determines the length of the tone played.
r = int Default: 44100. Sample rate. Determines the samples per second which are fed into the audio library.
s = int Default: 100*t. The number of segments the tone is divided in. Essentially: how many frequency changes the tone has.
l = float Default: 130.813 Hz. C3. This is the lowest tone the y series will be mapped to.
h = float Default: 2093.005 Hz. C7. This is the highest tone the y series will be mapped to.

w =
sine Outputs a sine waveform (default).
square Outputs a square waveform.
sawtooth Outputs a sawtooth waveform.
f =
fifth Adds a perfect fifth overtone.
yaranaika HARMONY
MATLAB plays the sound like ye olde days.\

Low-level functions

Read the docs (TODO) to use the underlying functions.
wave,samplerate = g2s.graph2wave takes in the arguments mentioned above and converts it to the required waveform. Assembles it into a ready-to-play array. Returns the waveform and the samplerate used.
g2s.wave2sound(wave,samplerate) takes the arguments of the previous function and plays them back.
y,x = g2s.argskwargcleanup(*args,**kwargs) verifies the x/y data format to it can be used in the rest of the functions.
f_list = g2s.freqlist(y,x,frequency_low,frequency_high,segments) creates a frequency table which is required to synthesize the desired tone.
wave,samplerate = g2s.synth(time,samplerate,segments,frequency_list,**kwargs) is the meat and potatoes of this module. Given information on how the samplerate and the segments look like, synthesizes a continuous waveform with the desired samplerate.
freq = g2s.midi2freq(mnn) takes in a midi note number (not from MIDI.org, needs login) and returns the frequency. A4 = 69 = 440 Hz.
mnn = g2s.freq2midi(freq) takes in a frequency and returns the midi note number as specified above.
mnn = g2s.midi2midi(mnn,delta_freq=0) changes the midi number by a given frequency. Does not output ints or check if the number is a real note or not.
freq = g2s.freq2freq(freq,delta_mnn=0) changes the frequency by a midi note interval.

Support

Shit's broken? Merge requests are open.

License

MIT

Why?

In one of my bachelors' courses (control theory), I was fucking around with MATLAB (hallowed be thy name) and at a certain point, I thought to myself:

"This graph needs sound"
- Crossed Omega, 2018

Obviously, this Lovecraftian horror of an idea took digital form rather quickly. After ample time, the first iteration was done - which sounded pretty horrible1. For the next three years, it remained untouched, left stewing, the entity itself anticipating a return. As I learnt Python, one of the first things I did, after giving the IT department a major headache, was rewrite graph2sound to be used as a Python library. Alas, my attempt to do so was not thwarted and thus stands before you an academic shitpostTM of genuine Dutch quality.
1: You can actually listen to what it sounded like by passing the MATLAB filter keyword f=MATLAB.

TODO

  • Add a small amount of filters to modify the waveforms
  • Rewrite the synth function, some of the arguments are redundant
  • Add an LFO
  • Add a sound library checker
  • Add proper data sanitation
  • Add multi-dimensional sounds
  • (Never ever) Rewrite this as a matplotlib backend

Known bugs

  • The synth function bugs out at some edge cases: for a function with the highest point at t=end, the final frequency is never played. Requires changes to the frequency list function.
  • The data validation misses lists such as [1,[1,2,3]] as a check for raggedness. This development is a pain in the bum.
  • The raggedness of arrays is still not handled perfectly, as ragged arrays cause all kinds of different errors.
  • The returned datatype of the ...2midi functions is a float; this should be an int and checked for if the frequency is actually a midi note.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

graph2sound-1.1.3.tar.gz (8.7 kB view hashes)

Uploaded Source

Built Distribution

graph2sound-1.1.3-py3-none-any.whl (8.0 kB view hashes)

Uploaded Python 3

Supported by

AWS AWS Cloud computing and Security Sponsor Datadog Datadog Monitoring Fastly Fastly CDN Google Google Download Analytics Microsoft Microsoft PSF Sponsor Pingdom Pingdom Monitoring Sentry Sentry Error logging StatusPage StatusPage Status page