Skip to content

Commit

Permalink
Sonification plugin tweaks (#3377)
Browse files Browse the repository at this point in the history
* support composite specctral regions, and ensure spectral regions are up-to-date n render

* sonification plugin options: rearrange, label, defaults.

* git push cleanup

* update max adio freq default

* make some of the advanced options inaccessible, add some better functionality for flux %ile removal

* take out tqdm loop

* strip out unused wl bounds parameters,

* update CHANGES

* Update jdaviz/configs/cubeviz/plugins/viewers.py with unit conversion formatting

Co-authored-by: P. L. Lim <[email protected]>

* Update jdaviz/configs/cubeviz/plugins/viewers.py with unit conversion formatting

Co-authored-by: P. L. Lim <[email protected]>

* format and explain RE @pllim suggestions

* pep8 formatting

* Update CHANGES.rst

Co-authored-by: P. L. Lim <[email protected]>

* Update CHANGES.rst

Co-authored-by: Jesse Averbukh <[email protected]>

* Remove whitespace

---------

Co-authored-by: James Trayford <[email protected]>
Co-authored-by: P. L. Lim <[email protected]>
Co-authored-by: Jesse Averbukh <[email protected]>
  • Loading branch information
4 people authored Jan 14, 2025
1 parent 2084d96 commit a8554a2
Show file tree
Hide file tree
Showing 5 changed files with 74 additions and 54 deletions.
2 changes: 1 addition & 1 deletion CHANGES.rst
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ New Features
Cubeviz
^^^^^^^

- Enhancements for the cube sonification plugin. [#3387]
- Enhancements for the cube sonification plugin. [#3377, #3387]

Imviz
^^^^^
Expand Down
10 changes: 5 additions & 5 deletions jdaviz/configs/cubeviz/plugins/cube_listener.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,8 +42,10 @@ def sonify_spectrum(spec, duration, overlap=0.05, system='mono', srate=44100, fm

data = {'spectrum': [spec], 'pitch': [1]}

# again, use maximal range for the mapped parameters
lims = {'spectrum': ('0', '100')}
# set range in spectral flux representing the maximum and minimum sound frequency power:
# 0 (numeric): absolute 0 in flux units, such that any flux above 0 will sound.
# '100' (string): 100th percentile (i.e. maximum value) in spectral flux.
lims = {'spectrum': (0, '100')}

# set up source
sources = Events(data.keys())
Expand All @@ -60,8 +62,7 @@ def sonify_spectrum(spec, duration, overlap=0.05, system='mono', srate=44100, fm

class CubeListenerData:
def __init__(self, cube, wlens, samplerate=44100, duration=1, overlap=0.05, buffsize=1024,
bdepth=16, wl_bounds=None, wl_unit=None, audfrqmin=50, audfrqmax=1500,
eln=False, vol=None):
bdepth=16, wl_unit=None, audfrqmin=50, audfrqmax=1500, eln=False, vol=None):
self.siglen = int(samplerate*(duration-overlap))
self.cube = cube
self.dur = duration
Expand All @@ -75,7 +76,6 @@ def __init__(self, cube, wlens, samplerate=44100, duration=1, overlap=0.05, buff
else:
self.atten_level = int(np.clip((vol/100)**2, MINVOL, 1))

self.wl_bounds = wl_bounds
self.wl_unit = wl_unit
self.wlens = wlens

Expand Down
29 changes: 23 additions & 6 deletions jdaviz/configs/cubeviz/plugins/sonify_data/sonify_data.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,13 +39,15 @@ class SonifyData(PluginTemplateMixin, DatasetSelectMixin, SpectralSubsetSelectMi
"""
template_file = __file__, "sonify_data.vue"

sample_rate = IntHandleEmpty(44100).tag(sync=True)
buffer_size = IntHandleEmpty(2048).tag(sync=True)
# Removing UI option to vary these for now
sample_rate = 44100 # IntHandleEmpty(44100).tag(sync=True)
buffer_size = 2048 # IntHandleEmpty(2048).tag(sync=True)
assidx = FloatHandleEmpty(2.5).tag(sync=True)
ssvidx = FloatHandleEmpty(0.65).tag(sync=True)
eln = Bool(False).tag(sync=True)
eln = Bool(True).tag(sync=True)
audfrqmin = FloatHandleEmpty(50).tag(sync=True)
audfrqmax = FloatHandleEmpty(1500).tag(sync=True)
audfrqmax = FloatHandleEmpty(1000).tag(sync=True)
use_pccut = Bool(True).tag(sync=True)
pccut = IntHandleEmpty(20).tag(sync=True)
volume = IntHandleEmpty(100).tag(sync=True)
stream_active = Bool(True).tag(sync=True)
Expand Down Expand Up @@ -91,12 +93,15 @@ def vue_sonify_cube(self, *args):
display_unit = self.spec_viewer.state.x_display_unit
min_wavelength = self.spectral_subset.selected_obj.lower.to_value(u.Unit(display_unit))
max_wavelength = self.spectral_subset.selected_obj.upper.to_value(u.Unit(display_unit))
self.flux_viewer.update_listener_wls(min_wavelength, max_wavelength, display_unit)
self.flux_viewer.update_listener_wls((min_wavelength, max_wavelength), display_unit)

# Ensure the current spectral region bounds are up-to-date at render time
self.update_wavelength_range(None)
# generate the sonified cube
self.flux_viewer.get_sonified_cube(self.sample_rate, self.buffer_size,
selected_device_index, self.assidx, self.ssvidx,
self.pccut, self.audfrqmin,
self.audfrqmax, self.eln)
self.audfrqmax, self.eln, self.use_pccut)

# Automatically select spectrum-at-spaxel tool
spec_at_spaxel_tool = self.flux_viewer.toolbar.tools['jdaviz:spectrumperspaxel']
Expand All @@ -106,6 +111,18 @@ def vue_start_stop_stream(self, *args):
self.stream_active = not self.stream_active
self.flux_viewer.stream_active = not self.flux_viewer.stream_active

@observe('spectral_subset_selected')
def update_wavelength_range(self, event):
if not hasattr(self, 'spec_viewer'):
return
display_unit = self.spec_viewer.state.x_display_unit
# is this spectral selection or the entire spectrum?
if hasattr(self.spectral_subset.selected_obj, "subregions"):
wlranges = self.spectral_subset.selected_obj.subregions
else:
wlranges = None
self.flux_viewer.update_listener_wls(wlranges, display_unit)

@observe('volume')
def update_volume_level(self, event):
self.flux_viewer.update_volume_level(event['new'])
Expand Down
56 changes: 28 additions & 28 deletions jdaviz/configs/cubeviz/plugins/sonify_data/sonify_data.vue
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,23 @@
:scroll_to.sync="scroll_to"
:disabled_msg="disabled_msg">

<j-plugin-section-header>Sonify Cube</j-plugin-section-header>
<j-plugin-section-header>Cube Pre-Sonification Options</j-plugin-section-header>
<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>

<v-row>
<j-docs-link>Choose the input cube, spectral subset and any advanced sonification options.</j-docs-link>
</v-row>
<plugin-dataset-select
:items="dataset_items"
:selected.sync="dataset_selected"
:show_if_single_entry="false"
label="Data"
api_hint="plg.dataset ="
:api_hints_enabled="api_hints_enabled"
hint="Select the data set."
/>
<plugin-subset-select
:items="spectral_subset_items"
:selected.sync="spectral_subset_selected"
Expand All @@ -24,34 +35,13 @@
:api_hints_enabled="api_hints_enabled"
hint="Select spectral region that defines the wavelength range."
/>

<v-row>
<v-expansion-panels accordion>
<v-expansion-panel>
<v-expansion-panel-header v-slot="{ open }">
<span style="padding: 6px">Advanced Sound Options</span>
</v-expansion-panel-header>
<v-expansion-panel-content class="plugin-expansion-panel-content">
<v-row>
<v-text-field
ref="sample_rate"
type="number"
label="Sample Rate"
v-model.number="sample_rate"
hint="The desired sample rate."
persistent-hint
></v-text-field>
</v-row>
<v-row>
<v-text-field
ref="buffer_size"
type="number"
label="Buffer Size"
v-model.number="buffer_size"
hint="The desired buffer size."
persistent-hint
></v-text-field>
</v-row>
<v-row>
<v-text-field
ref="audfrqmin"
Expand Down Expand Up @@ -92,13 +82,21 @@
persistent-hint
></v-text-field>
</v-row>
<v-row>
<v-row>
<v-switch
v-model="use_pccut"
label="Use Flux Percentile Cut?"
hint="Whether to only sonify flux above a min. percentile (else use absolute values)"
persistent-hint
></v-switch>
</v-row>
<v-row v-if="use_pccut">
<v-text-field
ref="pccut"
type="number"
label="Flux Percentile Cut"
label="Flux Percentile Cut Value"
v-model.number="pccut"
hint="The minimum flux percentile to be heard."
hint="The minimum percentile to be heard."
persistent-hint
></v-text-field>
</v-row>
Expand Down Expand Up @@ -138,20 +136,22 @@
Refresh Device List
</plugin-action-button>
</v-row>
<j-plugin-section-header>Live Sound Options</j-plugin-section-header>
<v-row>
<v-select
:menu-props="{ left: true }"
attach
:items="sound_devices_items"
v-model="sound_devices_selected"
label="Sound device"
hint="Device which sound will be output from. Must be selected BEFORE cube is sonified."
hint="Device which sound will be output from."
persistent-hint
></v-select>
</v-row>
<v-row>
Volume
<glue-throttled-slider label="Volume" wait="300" max="100" step="1" :value.sync="volume" hide-details class="no-hint" />
</v-row>
</v-row>
</j-tray-plugin>
</template>
</template>
31 changes: 17 additions & 14 deletions jdaviz/configs/cubeviz/plugins/viewers.py
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,8 @@ def __init__(self, *args, **kwargs):

self.sonified_cube = None
self.stream = None
self.sonification_wl_bounds = None

self.sonification_wl_ranges = None
self.sonification_wl_unit = None
self.volume_level = None
self.stream_active = True
Expand Down Expand Up @@ -113,8 +114,8 @@ def update_sonified_cube(self, x, y):
self.sonified_cube.newsig = self.sonified_cube.sigcube[x, y, :]
self.sonified_cube.cbuff = True

def update_listener_wls(self, w1, w2, wunit):
self.sonification_wl_bounds = (w1, w2)
def update_listener_wls(self, wranges, wunit):
self.sonification_wl_ranges = wranges
self.sonification_wl_unit = wunit

def update_sound_device(self, device_index):
Expand All @@ -133,18 +134,20 @@ def update_volume_level(self, level):
self.sonified_cube.atten_level = int(1/np.clip((level/100.)**2, MINVOL, 1))

def get_sonified_cube(self, sample_rate, buffer_size, device, assidx, ssvidx,
pccut, audfrqmin, audfrqmax, eln):
pccut, audfrqmin, audfrqmax, eln, use_pccut):
spectrum = self.active_image_layer.layer.get_object(statistic=None)
wlens = spectrum.wavelength.to('m').value
flux = spectrum.flux.value
self.sample_rate = sample_rate
self.buffer_size = buffer_size

if self.sonification_wl_bounds:
wl_unit = getattr(u, self.sonification_wl_unit)
si_wl_bounds = (self.sonification_wl_bounds * wl_unit).to('m')
wdx = np.logical_and(wlens >= si_wl_bounds[0].value,
wlens <= si_wl_bounds[1].value)
if self.sonification_wl_ranges:
wdx = np.zeros(wlens.size).astype(bool)
for r in self.sonification_wl_ranges:
# index just the spectral subregion
wdx = np.logical_or(wdx,
np.logical_and(wlens >= r[0].to_value(u.m),
wlens <= r[1].to_value(u.m)))
wlens = wlens[wdx]
flux = flux[:, :, wdx]

Expand All @@ -156,15 +159,15 @@ def get_sonified_cube(self, sample_rate, buffer_size, device, assidx, ssvidx,
# make a rough white-light image from the clipped array
whitelight = np.expand_dims(clipped_arr.sum(-1), axis=2)

# subtract any percentile cut
clipped_arr -= np.expand_dims(pc_cube, axis=2)
if use_pccut:
# subtract any percentile cut
clipped_arr -= np.expand_dims(pc_cube, axis=2)

# and re-clip
clipped_arr = np.clip(clipped_arr, 0, np.inf)
# and re-clip
clipped_arr = np.clip(clipped_arr, 0, np.inf)

self.sonified_cube = CubeListenerData(clipped_arr ** assidx, wlens, duration=0.8,
samplerate=sample_rate, buffsize=buffer_size,
wl_bounds=self.sonification_wl_bounds,
wl_unit=self.sonification_wl_unit,
audfrqmin=audfrqmin, audfrqmax=audfrqmax,
eln=eln, vol=self.volume_level)
Expand Down

0 comments on commit a8554a2

Please sign in to comment.