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 += daqconfig.getEnabledInputChannelSensitivities()
|
||||||
self.input_sensitivity = np.asarray(self.input_sensitivity)
|
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 += [
|
self.input_channel_names += [
|
||||||
ch.channel_name for ch in daqconfig.getEnabledInputChannels()]
|
ch.channel_name for ch in daqconfig.getEnabledInputChannels()]
|
||||||
|
|
||||||
self.input_samplerate = float(daqconfig.en_input_rate)
|
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
|
# Counters for the number of frames that have been coming in
|
||||||
self._aframectr = Atomic(0)
|
self._aframectr = Atomic(0)
|
||||||
@ -90,16 +107,6 @@ class AvStream:
|
|||||||
self._rtaudio = RtAudio(daqconfig.api)
|
self._rtaudio = RtAudio(daqconfig.api)
|
||||||
self.blocksize = self._rtaudio.openStream(self)
|
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):
|
def close(self):
|
||||||
self._rtaudio.closeStream()
|
self._rtaudio.closeStream()
|
||||||
|
@ -233,13 +233,14 @@ class TimeWeighting:
|
|||||||
slow = (1.0, 'Slow (1.0 s)')
|
slow = (1.0, 'Slow (1.0 s)')
|
||||||
tens = (10., '10 s')
|
tens = (10., '10 s')
|
||||||
infinite = (0, 'Infinite')
|
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)
|
types_all = (none, uufast, ufast, fast, slow, tens, infinite)
|
||||||
default = fast
|
default = fast
|
||||||
default_index = 3
|
default_index = 3
|
||||||
|
default_index_realtime = 1
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def fillComboBox(cb, all_=False):
|
def fillComboBox(cb, realtime=False):
|
||||||
"""
|
"""
|
||||||
Fill TimeWeightings to a combobox
|
Fill TimeWeightings to a combobox
|
||||||
|
|
||||||
@ -247,17 +248,23 @@ class TimeWeighting:
|
|||||||
cb: QComboBox to fill
|
cb: QComboBox to fill
|
||||||
"""
|
"""
|
||||||
cb.clear()
|
cb.clear()
|
||||||
if all_:
|
if realtime:
|
||||||
types = TimeWeighting.types_all
|
types = TimeWeighting.types_realtime
|
||||||
|
defindex = TimeWeighting.default_index_realtime
|
||||||
else:
|
else:
|
||||||
types = TimeWeighting.types
|
types = TimeWeighting.types_all
|
||||||
|
defindex = TimeWeighting.default_index
|
||||||
for tw in types:
|
for tw in types:
|
||||||
cb.addItem(tw[1], tw)
|
cb.addItem(tw[1], tw)
|
||||||
cb.setCurrentIndex(TimeWeighting.default_index)
|
|
||||||
|
cb.setCurrentIndex(defindex)
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def getCurrent(cb):
|
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:
|
class FreqWeighting:
|
||||||
|
@ -54,7 +54,7 @@ class BlockIter:
|
|||||||
"""Initialize a BlockIter object.
|
"""Initialize a BlockIter object.
|
||||||
|
|
||||||
Args:
|
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.i = 0
|
||||||
self.nblocks = f['audio'].shape[0]
|
self.nblocks = f['audio'].shape[0]
|
||||||
@ -98,6 +98,7 @@ def scaleBlockSens(block, sens):
|
|||||||
sensitivity: array of sensitivity coeficients for
|
sensitivity: array of sensitivity coeficients for
|
||||||
each channel
|
each channel
|
||||||
"""
|
"""
|
||||||
|
sens = np.asarray(sens)
|
||||||
assert sens.ndim == 1
|
assert sens.ndim == 1
|
||||||
assert sens.size == block.shape[1]
|
assert sens.size == block.shape[1]
|
||||||
if np.issubdtype(block.dtype.type, np.integer):
|
if np.issubdtype(block.dtype.type, np.integer):
|
||||||
@ -150,6 +151,7 @@ class Measurement:
|
|||||||
|
|
||||||
self.nblocks, self.blocksize, self.nchannels = f['audio'].shape
|
self.nblocks, self.blocksize, self.nchannels = f['audio'].shape
|
||||||
dtype = f['audio'].dtype
|
dtype = f['audio'].dtype
|
||||||
|
self.dtype = dtype
|
||||||
self.sampwidth = getSampWidth(dtype)
|
self.sampwidth = getSampWidth(dtype)
|
||||||
|
|
||||||
self.samplerate = f.attrs['samplerate']
|
self.samplerate = f.attrs['samplerate']
|
||||||
@ -267,20 +269,44 @@ class Measurement:
|
|||||||
self._prms = np.sqrt(pms)
|
self._prms = np.sqrt(pms)
|
||||||
return self._prms
|
return self._prms
|
||||||
|
|
||||||
def praw(self, block=None):
|
def rawData(self, block=None, channel=None):
|
||||||
"""Returns the uncalibrated acoustic pressure signal, converted to
|
"""Returns the raw signal, without any transformations applied
|
||||||
floating point acoustic pressure values [Pa]."""
|
|
||||||
|
args:
|
||||||
|
block: If specified a certain block is returned
|
||||||
|
|
||||||
|
"""
|
||||||
if block is not None:
|
if block is not None:
|
||||||
with self.file() as f:
|
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:
|
else:
|
||||||
blocks = []
|
blocks = []
|
||||||
with self.file() as f:
|
with self.file() as f:
|
||||||
for block in self.iterBlocks(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 = 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)
|
# Apply scaling (sensitivity, integer -> float)
|
||||||
blocks = self.scaleBlock(blocks)
|
blocks = self.scaleBlock(blocks)
|
||||||
@ -345,9 +371,10 @@ class Measurement:
|
|||||||
f.attrs['sensitivity'] = sens
|
f.attrs['sensitivity'] = sens
|
||||||
self._sens = 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
|
"""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:
|
Args:
|
||||||
fn: If given, this will be the filename to write to. If the
|
fn: If given, this will be the filename to write to. If the
|
||||||
@ -366,26 +393,21 @@ class Measurement:
|
|||||||
fn = self.fn
|
fn = self.fn
|
||||||
fn = os.path.splitext(fn)[0]
|
fn = os.path.splitext(fn)[0]
|
||||||
|
|
||||||
if '.wav' not in fn[-4:]:
|
if os.path.splitext(fn)[1] != '.wav':
|
||||||
fn += '.wav'
|
fn += '.wav'
|
||||||
|
|
||||||
if os.path.exists(fn) and not force:
|
if os.path.exists(fn) and not force:
|
||||||
raise RuntimeError(f'File already exists: {fn}')
|
raise RuntimeError(f'File already exists: {fn}')
|
||||||
|
|
||||||
with self.file() as f:
|
data = self.rawData()
|
||||||
|
|
||||||
audio = f['audio']
|
if normalize:
|
||||||
oldsampwidth = getSampWidth(audio.dtype)
|
maxabs = np.max(np.abs(data), axis=0)
|
||||||
|
data /= maxabs[np.newaxis, :]
|
||||||
|
|
||||||
max_ = 1.
|
if newsampwidth is not None:
|
||||||
if normalize:
|
# Convert to floats, then to new sample width
|
||||||
# Find maximum value
|
data = scaleBlockSens(data, self.sensitivity**0)
|
||||||
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 == 2:
|
if newsampwidth == 2:
|
||||||
newtype = np.int16
|
newtype = np.int16
|
||||||
@ -394,17 +416,12 @@ class Measurement:
|
|||||||
else:
|
else:
|
||||||
raise ValueError('Invalid sample width, should be 2 or 4')
|
raise ValueError('Invalid sample width, should be 2 or 4')
|
||||||
|
|
||||||
scalefac = 2**(8*(newsampwidth-oldsampwidth))
|
scalefac = 2**(8*(newsampwidth-1))-1
|
||||||
if normalize or isinstance(audio.dtype, float):
|
|
||||||
scalefac *= .01*max
|
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
|
@staticmethod
|
||||||
def fromtxt(fn,
|
def fromtxt(fn,
|
||||||
|
@ -673,6 +673,13 @@ cdef class Siggen:
|
|||||||
|
|
||||||
return output
|
return output
|
||||||
|
|
||||||
|
def progress(self):
|
||||||
|
"""
|
||||||
|
TODO: Should be implemented to return the current position in the
|
||||||
|
generator.
|
||||||
|
|
||||||
|
"""
|
||||||
|
return None
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def sineWave(d fs,d freq,d level_dB):
|
def sineWave(d fs,d freq,d level_dB):
|
||||||
|
Loading…
Reference in New Issue
Block a user