Updated exporting to wav file, NEEDS more testing. Added progress function to signal generator, which returns None by default
This commit is contained in:
parent
683c0b6566
commit
0a8949e0cf
@ -58,12 +58,29 @@ class AvStream:
|
||||
self.input_sensitivity += daqconfig.getEnabledInputChannelSensitivities()
|
||||
self.input_sensitivity = np.asarray(self.input_sensitivity)
|
||||
|
||||
# Fill in numpy data type, and sample width
|
||||
self.input_numpy_dtype = get_numpy_dtype_from_format_string(
|
||||
daqconfig.en_input_sample_format)
|
||||
|
||||
self.input_sampwidth = get_sampwidth_from_format_string(
|
||||
daqconfig.en_input_sample_format)
|
||||
|
||||
self.input_channel_names += [
|
||||
ch.channel_name for ch in daqconfig.getEnabledInputChannels()]
|
||||
|
||||
self.input_samplerate = float(daqconfig.en_input_rate)
|
||||
self.output_samplerate = float(daqconfig.en_output_rate)
|
||||
if self.duplex_mode:
|
||||
self.output_samplerate = float(daqconfig.en_input_rate)
|
||||
self.output_sampwidth = self.input_sampwidth
|
||||
self.output_numpy_dtype = self.input_numpy_dtype
|
||||
else:
|
||||
self.output_samplerate = float(daqconfig.en_output_rate)
|
||||
|
||||
self.output_sampwidth = get_sampwidth_from_format_string(
|
||||
daqconfig.en_output_sample_format)
|
||||
|
||||
self.output_numpy_dtype = get_numpy_dtype_from_format_string(
|
||||
daqconfig.en_output_sample_format)
|
||||
|
||||
# Counters for the number of frames that have been coming in
|
||||
self._aframectr = Atomic(0)
|
||||
@ -90,16 +107,6 @@ class AvStream:
|
||||
self._rtaudio = RtAudio(daqconfig.api)
|
||||
self.blocksize = self._rtaudio.openStream(self)
|
||||
|
||||
# Fill in numpy data type, and sample width
|
||||
self.input_numpy_dtype = get_numpy_dtype_from_format_string(
|
||||
daqconfig.en_input_sample_format)
|
||||
self.output_numpy_dtype = get_numpy_dtype_from_format_string(
|
||||
daqconfig.en_output_sample_format)
|
||||
|
||||
self.input_sampwidth = get_sampwidth_from_format_string(
|
||||
daqconfig.en_input_sample_format)
|
||||
self.output_sampwidth = get_sampwidth_from_format_string(
|
||||
daqconfig.en_output_sample_format)
|
||||
|
||||
def close(self):
|
||||
self._rtaudio.closeStream()
|
||||
|
@ -233,13 +233,14 @@ class TimeWeighting:
|
||||
slow = (1.0, 'Slow (1.0 s)')
|
||||
tens = (10., '10 s')
|
||||
infinite = (0, 'Infinite')
|
||||
types = (none, uufast, ufast, fast, slow, tens)
|
||||
types_realtime = (ufast, fast, slow, tens, infinite)
|
||||
types_all = (none, uufast, ufast, fast, slow, tens, infinite)
|
||||
default = fast
|
||||
default_index = 3
|
||||
default_index_realtime = 1
|
||||
|
||||
@staticmethod
|
||||
def fillComboBox(cb, all_=False):
|
||||
def fillComboBox(cb, realtime=False):
|
||||
"""
|
||||
Fill TimeWeightings to a combobox
|
||||
|
||||
@ -247,17 +248,23 @@ class TimeWeighting:
|
||||
cb: QComboBox to fill
|
||||
"""
|
||||
cb.clear()
|
||||
if all_:
|
||||
types = TimeWeighting.types_all
|
||||
if realtime:
|
||||
types = TimeWeighting.types_realtime
|
||||
defindex = TimeWeighting.default_index_realtime
|
||||
else:
|
||||
types = TimeWeighting.types
|
||||
types = TimeWeighting.types_all
|
||||
defindex = TimeWeighting.default_index
|
||||
for tw in types:
|
||||
cb.addItem(tw[1], tw)
|
||||
cb.setCurrentIndex(TimeWeighting.default_index)
|
||||
|
||||
cb.setCurrentIndex(defindex)
|
||||
|
||||
@staticmethod
|
||||
def getCurrent(cb):
|
||||
return TimeWeighting.types_all[cb.currentIndex()]
|
||||
if cb.count() == len(TimeWeighting.types_realtime):
|
||||
return TimeWeighting.types_realtime[cb.currentIndex()]
|
||||
else:
|
||||
return TimeWeighting.types_all[cb.currentIndex()]
|
||||
|
||||
|
||||
class FreqWeighting:
|
||||
|
@ -54,7 +54,7 @@ class BlockIter:
|
||||
"""Initialize a BlockIter object.
|
||||
|
||||
Args:
|
||||
faudio: Audio dataset in the h5 file, accessed as f['audio']
|
||||
f: Audio dataset in the h5 file, accessed as f['audio']
|
||||
"""
|
||||
self.i = 0
|
||||
self.nblocks = f['audio'].shape[0]
|
||||
@ -98,6 +98,7 @@ def scaleBlockSens(block, sens):
|
||||
sensitivity: array of sensitivity coeficients for
|
||||
each channel
|
||||
"""
|
||||
sens = np.asarray(sens)
|
||||
assert sens.ndim == 1
|
||||
assert sens.size == block.shape[1]
|
||||
if np.issubdtype(block.dtype.type, np.integer):
|
||||
@ -150,6 +151,7 @@ class Measurement:
|
||||
|
||||
self.nblocks, self.blocksize, self.nchannels = f['audio'].shape
|
||||
dtype = f['audio'].dtype
|
||||
self.dtype = dtype
|
||||
self.sampwidth = getSampWidth(dtype)
|
||||
|
||||
self.samplerate = f.attrs['samplerate']
|
||||
@ -267,20 +269,44 @@ class Measurement:
|
||||
self._prms = np.sqrt(pms)
|
||||
return self._prms
|
||||
|
||||
def praw(self, block=None):
|
||||
"""Returns the uncalibrated acoustic pressure signal, converted to
|
||||
floating point acoustic pressure values [Pa]."""
|
||||
def rawData(self, block=None, channel=None):
|
||||
"""Returns the raw signal, without any transformations applied
|
||||
|
||||
args:
|
||||
block: If specified a certain block is returned
|
||||
|
||||
"""
|
||||
if block is not None:
|
||||
with self.file() as f:
|
||||
blocks = f['audio'][block]
|
||||
if channel is not None:
|
||||
blocks = f['audio'][block][:, [channel]]
|
||||
else:
|
||||
blocks = f['audio'][block]
|
||||
else:
|
||||
blocks = []
|
||||
with self.file() as f:
|
||||
for block in self.iterBlocks(f):
|
||||
blocks.append(block)
|
||||
if channel is not None:
|
||||
blocks.append(block[:, [channel]])
|
||||
else:
|
||||
blocks.append(block)
|
||||
|
||||
blocks = np.asarray(blocks)
|
||||
blocks = blocks.reshape(self.nblocks * self.blocksize,
|
||||
self.nchannels)
|
||||
|
||||
if channel is None:
|
||||
blocks = blocks.reshape((self.nblocks * self.blocksize,
|
||||
self.nchannels))
|
||||
else:
|
||||
blocks = blocks.reshape((self.nblocks * self.blocksize,
|
||||
1))
|
||||
return blocks
|
||||
|
||||
def praw(self, block=None):
|
||||
"""Returns the uncalibrated acoustic pressure signal, but the
|
||||
sensitivity is applied, converted to floating point acoustic
|
||||
pressure values [Pa]."""
|
||||
|
||||
blocks = self.rawData(block)
|
||||
|
||||
# Apply scaling (sensitivity, integer -> float)
|
||||
blocks = self.scaleBlock(blocks)
|
||||
@ -345,9 +371,10 @@ class Measurement:
|
||||
f.attrs['sensitivity'] = sens
|
||||
self._sens = sens
|
||||
|
||||
def exportAsWave(self, fn=None, force=False, newsampwidth=2, normalize=True):
|
||||
def exportAsWave(self, fn=None, force=False, newsampwidth=None, normalize=True):
|
||||
"""Export measurement file as wave. In case the measurement data is
|
||||
stored as floats, the values are scaled between 0 and 1.
|
||||
stored as floats, the values are scaled to the proper integer (PCM)
|
||||
data format.
|
||||
|
||||
Args:
|
||||
fn: If given, this will be the filename to write to. If the
|
||||
@ -366,26 +393,21 @@ class Measurement:
|
||||
fn = self.fn
|
||||
fn = os.path.splitext(fn)[0]
|
||||
|
||||
if '.wav' not in fn[-4:]:
|
||||
if os.path.splitext(fn)[1] != '.wav':
|
||||
fn += '.wav'
|
||||
|
||||
if os.path.exists(fn) and not force:
|
||||
raise RuntimeError(f'File already exists: {fn}')
|
||||
|
||||
with self.file() as f:
|
||||
data = self.rawData()
|
||||
|
||||
audio = f['audio']
|
||||
oldsampwidth = getSampWidth(audio.dtype)
|
||||
if normalize:
|
||||
maxabs = np.max(np.abs(data), axis=0)
|
||||
data /= maxabs[np.newaxis, :]
|
||||
|
||||
max_ = 1.
|
||||
if normalize:
|
||||
# Find maximum value
|
||||
for block in self.iterBlocks(f):
|
||||
blockmax = np.max(np.abs(block))
|
||||
max_ = blockmax if blockmax > max_ else max_
|
||||
# Scale with maximum value only if we have a nonzero maximum value.
|
||||
if max_ == 0.:
|
||||
max_ = 1.
|
||||
if newsampwidth is not None:
|
||||
# Convert to floats, then to new sample width
|
||||
data = scaleBlockSens(data, self.sensitivity**0)
|
||||
|
||||
if newsampwidth == 2:
|
||||
newtype = np.int16
|
||||
@ -394,17 +416,12 @@ class Measurement:
|
||||
else:
|
||||
raise ValueError('Invalid sample width, should be 2 or 4')
|
||||
|
||||
scalefac = 2**(8*(newsampwidth-oldsampwidth))
|
||||
if normalize or isinstance(audio.dtype, float):
|
||||
scalefac *= .01*max
|
||||
scalefac = 2**(8*(newsampwidth-1))-1
|
||||
|
||||
data = (data*scalefac).astype(newtype)
|
||||
|
||||
wavfile.write(fn, self.samplerate, data)
|
||||
|
||||
with wave.open(fn, 'w') as wf:
|
||||
wf.setparams((self.nchannels, self.sampwidth,
|
||||
self.samplerate, 0, 'NONE', 'NONE'))
|
||||
for block in self.iterBlocks(f):
|
||||
# Convert block to integral data type
|
||||
block = (block*scalefac).astype(newtype)
|
||||
wf.writeframes(np.asfortranarray(block).tobytes())
|
||||
|
||||
@staticmethod
|
||||
def fromtxt(fn,
|
||||
|
@ -673,6 +673,13 @@ cdef class Siggen:
|
||||
|
||||
return output
|
||||
|
||||
def progress(self):
|
||||
"""
|
||||
TODO: Should be implemented to return the current position in the
|
||||
generator.
|
||||
|
||||
"""
|
||||
return None
|
||||
|
||||
@staticmethod
|
||||
def sineWave(d fs,d freq,d level_dB):
|
||||
|
Loading…
Reference in New Issue
Block a user