james-trayford / strauss

Sonification Tools and Resources for Analysis Using Sound Synthesis
Apache License 2.0
33 stars 4 forks source link

Unable to run strauss/examples #3

Closed pedro-acunha closed 1 year ago

pedro-acunha commented 1 year ago

I followed the instructions to install strauss as provided in the github page, using the following commands: git clone https://github.com/james-trayford/strauss.git strauss cd strauss pip install .

When running the examples in strauss/examples, I am unable to successfully run the cell with Sonification function. It gets stuck with this output:

0%| | 0/1 [00:00<?, ?it/s]

This can also be seen below, for the SonifyingData1D example: image

I did try to run with two different machines (similar setups), but the problem remains in both.

I am running strauss in a machine with:

james-trayford commented 1 year ago

Hi @pedro-acunha , thanks for raising this - sorry to hear you've been having trouble running. I'm not reproducing this in my setup, and the fact that it is a hang rather than an error + traceback makes it a bit harder to work out what's going on.

It would be very helpful if you could try getting a bit more info on the issue. First, you could try running the isolated code block that should do the same thing as the example notebook:

import matplotlib.pyplot as plt
from strauss.sonification import Sonification
from strauss.sources import Objects
from strauss import channels
from strauss.score import Score
from strauss.generator import Synthesizer
import IPython.display as ipd
import os
from scipy.interpolate import interp1d
import numpy as np

# seed the randoms...
np.random.seed(0)

# construct arrays of size N for x and y...
N = 300
x = np.linspace(0,1,N)
y = np.zeros(N)

# define a Gaussian function...
gauss = lambda x, m, s: np.exp(-(x-m)**2/s) 

# place some randomised gaussians...
for i in range(10):
    a,b,c = np.random.random(3)
    y += gauss(x, b, 1e-3*c) * a ** 3

# now add some noise and normalise
y += np.random.random(N) * y.mean()
y /= y.max()*1.2
y += 0.15

plt.plot(x,y)
plt.ylabel('Some dependent Variable')
plt.xlabel('Some independent Variable')
plt.savefig("test_plot.png")

# specify audio system (e.g. mono, stereo, 5.1, ...)
system = "stereo"

# length of the sonification in s
length = 15.

# set up synth and turn on LP filter
generator = Synthesizer()
generator.load_preset('pitch_mapper')
generator.preset_details('pitch_mapper')

notes = [["A2"]]
score =  Score(notes, length)

data = {'pitch':1.,
        'time_evo':x,
        'phi':(x*0.5+0.25) % 1,
        'theta':0.5,
        'pitch_shift':y}

# set up source
sources = Objects(data.keys())
sources.fromdict(data)
sources.apply_mapping_functions()

soni = Sonification(score, sources, generator, system)
soni.render()
soni.save_stereo('test_audio.wav')

you could save this script as e.g. strauss_test.py and try running this in the terminal - does it still still hang? If it runs ok you should get the plot and audio output as .png and .wav files respectively

if it runs OK in this case it's likely some kind of problem with the jupyter environment (perhaps how you have it set up). If it still hangs, could try sending it a keyboard interrupt (ctrl+c on your system) to hopefully get a traceback of where the code is when it hangs. If that doesn't work either we can look into using the faulthandler module to dig a bit further.

pedro-acunha commented 1 year ago

Hi @james-trayford, thank you for your quick answer.

I followed your instructions and the problem persisted. I couldn't do a keyboard interrupt to stop the job, I really had to kill the process. Otherwise, it still hangs.

I used faulthandler to try to debug. Here is what I got:

soni.render()
0%|                                                                                               | 0/1 [00:00<?, ?it/s]

So, it seems the code gets stuck in: soni.render().

james-trayford commented 1 year ago

OK, so if you are using a new enough python version (> 3.5 I think) you could try adding

import faulthandler
import signal

faulthandler.register(signal.SIGUSR1.value)

to the top matter of the script. Then running like this

$ python strauss_test.py &
[<n1>] <n2>

will send the hanging code into the background, so running

$ kill -s SIGUSR1 <n2>

should print the location of the hang (but not kill the code). what is this?

you can then kill the process properly with

$ kill <n2>
pedro-acunha commented 1 year ago

Here is a screenshot, from the output.

image

james-trayford commented 1 year ago

huh, that's weird, the kill -s... command is not printing anything 🤔 .

When I make the sonification very long (to simulate a hang) and do this approach I get something like

$ kill -s SIGUSR1 14744
File "/users/trayford/code/strauss_clean/src/strauss/generator.py", line 485 in play
  File "/users/trayford/code/strauss_clean/src/strauss/sonification.py", line 132 in render
  File "runstrauss.py", line 69 in <module>

suggesting the code is stuck in File "/users/trayford/code/strauss_clean/src/strauss/generator.py", line 485 in play

let me think about this a bit more...

james-trayford commented 1 year ago

quick follow up, apparently the SIGUSR1 may not be correctly defined in python in some versions, and can hard code it. using $ man 7 signal gives the signal codes on my system

       Signal     Value     Action   Comment
       ──────────────────────────────────────────────────────────────────────
       SIGHUP        1       Term    Hangup detected on controlling terminal
                                     or death of controlling process
       SIGINT        2       Term    Interrupt from keyboard
       SIGQUIT       3       Core    Quit from keyboard
       SIGILL        4       Core    Illegal Instruction
       SIGABRT       6       Core    Abort signal from abort(3)
       SIGFPE        8       Core    Floating point exception
       SIGKILL       9       Term    Kill signal
       SIGSEGV      11       Core    Invalid memory reference
       SIGPIPE      13       Term    Broken pipe: write to pipe with no
                                     readers
       SIGALRM      14       Term    Timer signal from alarm(2)
       SIGTERM      15       Term    Termination signal
       SIGUSR1   30,10,16    Term    User-defined signal 1                               <-----------------
       SIGUSR2   31,12,17    Term    User-defined signal 2
       SIGCHLD   20,17,18    Ign     Child stopped or terminated
       SIGCONT   19,18,25    Cont    Continue if stopped
       SIGSTOP   17,19,23    Stop    Stop process
       SIGTSTP   18,20,24    Stop    Stop typed at terminal
       SIGTTIN   21,21,26    Stop    Terminal input for background process
       SIGTTOU   22,22,27    Stop    Terminal output for background process

shows I can use e.g. 10 for this signal (may be different on different systems) so in the script can change faulthandler.register(10) to work instead (for me this gives the same output when killed with $ kill -s SIGUSR1 <n2>)

pedro-acunha commented 1 year ago

I was able to get the output from kill -s ...:

Thread 0x00007f29cea03640 (most recent call first):
  File "/usr/lib/python3.10/threading.py", line 324 in wait
  File "/usr/lib/python3.10/threading.py", line 607 in wait
  File "/home/pcunha/.local/lib/python3.10/site-packages/tqdm/_monitor.py", line 60 in run
  File "/usr/lib/python3.10/threading.py", line 1016 in _bootstrap_inner
  File "/usr/lib/python3.10/threading.py", line 973 in _bootstrap

Current thread 0x00007f2a419dc000 (most recent call first):
  File "/usr/lib/python3/dist-packages/scipy/interpolate/_interpolate.py", line 699 in _evaluate
  File "/usr/lib/python3/dist-packages/scipy/interpolate/_polyint.py", line 78 in __call__
  File "/home/pcunha/.local/lib/python3.10/site-packages/strauss/generator.py", line 479 in play
  File "/home/pcunha/.local/lib/python3.10/site-packages/strauss/sonification.py", line 131 in render
  File "/home/pcunha/strauss/strauss_test.py", line 72 in <module>
james-trayford commented 1 year ago

Ah great, some progress! my guess is this is just hanging here, but you can also re-run the kill -s command before killing it properly to see if it goes anywhere else (i.e. stuck in some loop)

it looks like this is actually happening in scipy when an interpolated function is evaluated, via L479 of the strauss submodule:

$ grep -n -C1 pitch_shift ../strauss_clean/src/strauss/generator.py 
477-        pindex  = np.zeros(samples.size)
478:        if callable(params['pitch_shift']):
479:            pindex += params['pitch_shift'](sstream.sampfracs)/12.
480:        elif params['pitch_shift'] != 0:
481:            pindex += params['pitch_shift']/12.
482-        if params['pitch_lfo']['use']:

This should be fast, as it's just evaluating a pre-defined interpolation function. making the sonification shorter should make it faster still. it might be worth testing the scipy.interp1d function to see if you have the same problem outside of strauss from the /usr/lib/python3/dist-packages/scipy scipy version (which looks like it was installed separately from strauss). I'll have a think about what else could be happening

pedro-acunha commented 1 year ago

Indeed, it seems like I am having problems with scipy.interp1d function. Even the most simple example provided in the package documentation, is left hanging:

x = np.arange(0, 10)
y = np.exp(-x/3.0)
f = interpolate.interp1d(x, y)

xnew = np.arange(0, 9, 0.1)
ynew = f(xnew)   # use interpolation function returned by `interp1d`
plt.plot(x, y, 'o', xnew, ynew, '-')
plt.show()

I already removed scipy, using pip, and installed it again along side strauss. The problem still persisted. Are you using the version scipy-1.9.3 or a previous version?

I will look into this in more detailed.

james-trayford commented 1 year ago

Hi @pedro-acunha - OK, seems like we've isolated the problem to that install of scipy so I'm going to temporarily close this as a strauss issue, though can reopen if the problem persists beyond scipy. Might be worth opening an issue on the scipy repo.

Looks like I was using scipy-1.7.0 in my setup. I tried a fresh strauss install using scipy-1.9.3 too - that seems to work for me as well. Think you can see exactly what scipy version and install location strauss is using with

from strauss.generator import scipy
print(scipy.__version__, scipy.__file__)

You could also try instead installing strauss within a virtual environment to make an isolated environment for strauss - think it's possible to use these virtual environments within jupyter too if you pip install jupyter within the environment.

Hope some of that helps, please follow up if you make any progress or problems persist.

pedro-acunha commented 1 year ago

Hi @james-trayford, I was able to get it working on a virtual environment. I am still puzzled on why it is not working outside. I opened an issue in the scipy repo, either way.

Thank you very much for all of your help :)

james-trayford commented 1 year ago

Great 😁! Hope that works out for you and whatever scipy issue gets sorted out - feel free to follow up with further issues or feedback and thanks for using strauss!

pedro-acunha commented 1 year ago

Hi @james-trayford, It was all due to a glitch in the installation. Fixed it and now every examples work! Thank you very much for the cool code, really appreciate that you share this with the community.

james-trayford commented 1 year ago

Excellent, thanks, hope you have fun using it and I'm always very happy to see new applications!