Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Sonify plugin updates #3269

Open
wants to merge 47 commits into
base: main
Choose a base branch
from

Conversation

javerbukh
Copy link
Contributor

@javerbukh javerbukh commented Nov 6, 2024

Description

This pull request takes the work from james-trayford#3 and moves it into a PR for Jdaviz. There is now a plugin called "Sonify Data" which contains configuration options for sonifying a data cube. There are also options for which speaker the sound will come from, volume, and to start/stop the stream so the spectrum-at-spaxel tool can work as usual after sonifying the cube.

Change log entry

  • Is a change log needed? If yes, is it added to CHANGES.rst? If you want to avoid merge conflicts,
    list the proposed change log here for review and add to CHANGES.rst before merge. If no, maintainer
    should add a no-changelog-entry-needed label.
  • Add Sonify Data plugin which uses the Strauss package to turn a data cube into sound. [Sonify plugin updates #3269]

Checklist for package maintainer(s)

This checklist is meant to remind the package maintainer(s) who will review this pull request of some common things to look for. This list is not exhaustive.

  • Are two approvals required? Branch protection rule does not check for the second approval. If a second approval is not necessary, please apply the trivial label.
  • Do the proposed changes actually accomplish desired goals? Also manually run the affected example notebooks, if necessary.
  • Do the proposed changes follow the STScI Style Guides?
  • Are tests added/updated as required? If so, do they follow the STScI Style Guides?
  • Are docs added/updated as required? If so, do they follow the STScI Style Guides?
  • Did the CI pass? If not, are the failures related?
  • Is a milestone set? Set this to bugfix milestone if this is a bug fix and needs to be released ASAP; otherwise, set this to the next major release milestone. Bugfix milestone also needs an accompanying backport label.
  • After merge, any internal documentations need updating (e.g., JIRA, Innerspace)?

javerbukh and others added 25 commits July 31, 2024 17:27
- Fix sigcube alignment with other cubes [get rid of transpose]
- Add percentile cut option that enables some rough feature isolation without full continuum subtraction
- Protect against cubes w/NaNs
@github-actions github-actions bot added cubeviz plugin Label for plugins common to multiple configurations labels Nov 6, 2024
@javerbukh javerbukh added this to the 4.1 milestone Nov 6, 2024
@javerbukh javerbukh force-pushed the sonify-plugin-updates branch from 57ee976 to cc51183 Compare November 7, 2024 15:08
@camipacifici
Copy link
Contributor

Looks good! A couple of questions:

  • (this is my ignorance) if the package is not installed, why do the instructions say 'pip install .[strauss]' and not just 'pip install strauss'?
  • probably also the start/stop streaming should be grayed out when strauss is not installed
  • when sonifying, should the spinner be also at the top of the app like in model fitting?
    Works really well!

@javerbukh
Copy link
Contributor Author

@camipacifici

if the package is not installed, why do the instructions say 'pip install .[strauss]' and not just 'pip install strauss'?

The released version of strauss does not include all the fixes to get it working with Jdaviz so instead we use "strauss@git+https://github.com/james-trayford/strauss@equal_loudness_normalisation" in the pyproject.toml file. pip install .[strauss] downloads that package from that path.

probably also the start/stop streaming should be grayed out when strauss is not installed

Good call!

when sonifying, should the spinner be also at the top of the app like in model fitting?

I'm not sure I see what you mean. When testing with model fitting, I see the spinner in the same location as the Sonify Data button.

docs/cubeviz/plugins.rst Outdated Show resolved Hide resolved
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is it possible to keep any of this logic in the plugin itself? It's very specific to this single plugin (right now - maybe eventually we'll have more generic audio support). Right now, if I understand correctly, there is a limitation of a single sonified cube at a time (running sonify replaces any previous instances), so I don't see much benefit to having this be specifically per-viewer logic.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The audio stream needs to be connected to the viewer (at least with the current implementation) so that certain mouse events can turn the stream on or off. The audified cube needs to 'live' somewhere and right now the viewer is the best place for that. Once we decouple the sonified cube from the spectrum-at-spaxel tool and have it as a separate data object, this should be doable. I will create a follow-up ticket and include that context.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we can connect the event handling to the tool/viewer but still keep all the logic in the plugin (I think). Its just nice to keep all the code in one place and not "pollute" the viewer code with logic that only applies to one plugin (which is intended to be modular).

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok, that effort can now be tracked in #3330 and #3331 .

Comment on lines +7 to +14
try:
from strauss.sonification import Sonification
from strauss.sources import Events
from strauss.score import Score
from strauss.generator import Spectralizer
from tqdm import tqdm
except ImportError:
pass
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is it possible for any of these imports to fail but there still be a call to any of the logic below (either manual API calls or if none of the imports in the plugin itself fail and so _has_strauss = True but tqdm failed to import, for example)?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

None of the methods are public API and tqdm is a dependency of Strauss, but I can see that being an issue down the road. Is it alright for this to be a follow-up?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wonder if we can either put the import tqdm in the else or import _has_strauss from the plugin. But yes, probably can be a follow-up.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think if we decide to move most of the cube_listener.py code to the sonify plugin then we can include this effort in #3330 .

sys.stderr = old_stderr


def audify_spectrum(spec, duration, overlap=0.05, system='mono', srate=44100, fmin=40, fmax=1300,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this whole function is never reached by tests - is it possible to add coverage (or if out of scope, can we create a follow-up)?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

on closer look, it seems that the existing tests added in this PR are probably never actually running on CI because of dependencies. Can we add strauss to at least one of the runners so they do run?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can I add the Strauss package to all in pyproject.toml or should I create a new workflow for Strauss?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

let's discuss - it might be time to make a convention for this and do the same thing for footprints, Roman, and strauss.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

any resolution to the test coverage situation?

return soni.loop_channels['0'].values


class CubeListenerData:
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

same as above re test coverage

Comment on lines +5 to +7
:uses_active_status="uses_active_status"
@plugin-ping="plugin_ping($event)"
:keep_active.sync="keep_active"
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

instead of using the spectrum at spaxel tool, we could have the mouseover events enabled whenever the plugin is active 🤔 (could be a follow-up, especially given the conversation about potential future audio layers)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, follow-up effort tracked at #3331

jdaviz/configs/cubeviz/plugins/sonify_data/sonify_data.vue Outdated Show resolved Hide resolved
Comment on lines +12 to +15
<v-alert v-if="!has_strauss" type="warning" style="margin-left: -12px; margin-right: -12px">
To use Sonify Data, install strauss and restart Jdaviz. You can do this by running `pip install .[strauss]`
in the command line and then launching Jdaviz.
</v-alert>
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can use the disabled_msg traitlet instead and then the rest of the plugin will automatically not be shown without having to v-else everything else.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We would lose the yellow alert box but that's fine with me.

@@ -134,6 +137,8 @@ filterwarnings = [
"ignore:The unit 'Angstrom' has been deprecated in the VOUnit standard\\. Suggested.* 0\\.1nm\\.",
"ignore:((.|\n)*)Sentinel is not a public part of the traitlets API((.|\n)*)",
"ignore:datetime\\.datetime\\.utcfromtimestamp:DeprecationWarning", # asdf + dateutil<=2.8.2 + Python 3.12
"ignore:'audioop' is deprecated and slated for removal in Python 3.13",
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

do we need a follow-up to address this before 3.13 comes out? What should be used instead?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is an upstream issue, I can make a note of these warnings and pass them along to the Strauss team.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Follow-up for tracking that information #3332

@@ -134,6 +137,8 @@ filterwarnings = [
"ignore:The unit 'Angstrom' has been deprecated in the VOUnit standard\\. Suggested.* 0\\.1nm\\.",
"ignore:((.|\n)*)Sentinel is not a public part of the traitlets API((.|\n)*)",
"ignore:datetime\\.datetime\\.utcfromtimestamp:DeprecationWarning", # asdf + dateutil<=2.8.2 + Python 3.12
"ignore:'audioop' is deprecated and slated for removal in Python 3.13",
"ignore:Importing display from IPython.core.display is deprecated since IPython 7.14, please import from IPython.display",
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

where does this need to be fixed? Is there an upstream issue?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same as above.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

self.cursig[:] = self.sigcube[self.idx1, self.idx2, :]
self.newsig[:] = self.cursig[:]
t1 = time.time()
print(f"Took {t1-t0}s to process {self.cube.shape[0]*self.cube.shape[1]} spaxels")
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is this print statement a temporary way to inform the user or for debugging?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it was originally for debugging but now it might be useful for logging performance. cube_listener.py is pretty isolated right now but maybe it could be a snackbar message once we figure out where this code should live.

Copy link
Collaborator

@rosteen rosteen left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I left a couple minor comments, I think given the follow up tickets that already exist (and the additional one I suggested) I'd be ok merging this. I'm going to sit on it until tomorrow morning and let my thoughts stew before I approve.

jdaviz/configs/cubeviz/plugins/sonify_data/sonify_data.py Outdated Show resolved Hide resolved
Comment on lines +120 to +121
for i in tqdm(range(self.cube.shape[0])):
for j in range(self.cube.shape[1]):
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Any time I see nested for loops it makes me twitch - I forget how long this takes, but I would advocate for a follow-up ticket to multiprocess this (given that it doesn't seem amenable to vectorization) like we do for cube model fitting.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good call, tracking that issue at #3339 .

wsrt = np.sort([w1, w2])
self.wl_bounds = tuple(wsrt)

def audify_cube(self):
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Given that audify_spectrum is outside the class as an independent function, and this pretty much just uses self.cube from self, I wonder if this could/should be pulled out into a function that takes cube as input and returns what's needed to the class 🤔. Probably not important, it just seemed a little wonky to have audify_spectrum outside this class and this inside. Maybe it's just that audify_spectrum would be better off in the sonify plugin file.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah I think that is what #3330 is hoping to fix by moving some/all of the cube_listener.py code into the sonify data plugin.

@javerbukh javerbukh requested review from rosteen and kecnry December 6, 2024 17:35
Copy link
Collaborator

@rosteen rosteen left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Still needs a changelog entry, but I'm approving assuming you'll add that before merging.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is hard to test because standalone jobs are all currently failing on main.

@@ -82,6 +82,9 @@ docs = [
roman = [
"roman_datamodels>=0.17.1",
]
strauss = [
"strauss@git+https://github.com/james-trayford/strauss@equal_loudness_normalisation"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

PyPI won't take this. Strauss needs a stable release or you can only install it in tox.ini. But if the latter, Jdaviz better still be able to run if strauss is not installed.

I think you also introduced a dependence to tqdm but it is not declared here.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok I reached out to James from the Strauss team to see if we can get a release of Strauss out soon. Should I add tqdm to our dependencies if it only is used for Strauss?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You should add it under the strauss directive here, no? I see you explicitly trying to import it in cube_listener.py.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

p.s. Do we really need it? Isn't it for command line progress bar? But we are a GUI program here, not TUI.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's something else I can bring up with the Strauss team.

@pllim
Copy link
Contributor

pllim commented Dec 9, 2024

In terms of CI, you can set something up like this for Roman:

ci_cron_tests_stable_roman:

Then we can slap "Extra CI" label on this PR.

@pllim pllim added the Build standalone Additional pylons label Dec 9, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Build standalone Additional pylons cubeviz plugin Label for plugins common to multiple configurations
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants