Updated exporting to wav file, NEEDS more testing. Added progress function to signal generator, which returns None by default

This commit is contained in:
Anne de Jong 2020-08-20 10:23:24 +02:00
parent 683c0b6566
commit 0a8949e0cf
4 changed files with 89 additions and 51 deletions

View File

@ -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()

View File

@ -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:

View File

@ -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,

View File

@ -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):