Compare commits
9 Commits
42e8ebf1d2
...
ee3c41fe70
Author | SHA1 | Date | |
---|---|---|---|
|
ee3c41fe70 | ||
|
c017cafe51 | ||
|
a163756f04 | ||
|
9ecb4d41b4 | ||
|
349a6be063 | ||
|
38783413b6 | ||
|
1254547ce0 | ||
|
9b998596b7 | ||
|
49ef663176 |
@ -18,7 +18,7 @@ class AudioDevice(object):
|
|||||||
self.bufL = []
|
self.bufL = []
|
||||||
self.bufIdx = -1
|
self.bufIdx = -1
|
||||||
self.srate = 0
|
self.srate = 0
|
||||||
self.ch_cnt = 1
|
self.ch_cnt = 2
|
||||||
|
|
||||||
|
|
||||||
def setup( self, **kwargs ):
|
def setup( self, **kwargs ):
|
||||||
@ -63,11 +63,11 @@ class AudioDevice(object):
|
|||||||
}
|
}
|
||||||
|
|
||||||
return d
|
return d
|
||||||
|
|
||||||
def get_port_list( self, inFl ):
|
def get_port_list( self, inFl ):
|
||||||
|
|
||||||
devLabelL = str(sd.query_devices()).split("\n")
|
devLabelL = str(sd.query_devices()).split("\n")
|
||||||
|
|
||||||
portL = []
|
portL = []
|
||||||
for i,dev in enumerate(sd.query_devices()):
|
for i,dev in enumerate(sd.query_devices()):
|
||||||
isInputFl = dev['max_input_channels'] > 0
|
isInputFl = dev['max_input_channels'] > 0
|
||||||
|
82
README.md
@ -1,2 +1,84 @@
|
|||||||
* Picadae Calibration Programs
|
* Picadae Calibration Programs
|
||||||
|
|
||||||
|
sudo dnf install python3-devel jack-audio-connection-kit-devel
|
||||||
|
|
||||||
|
|
||||||
|
prerequisites:
|
||||||
|
pyyaml
|
||||||
|
sounddevice
|
||||||
|
soundfile
|
||||||
|
pthon-rtmidi
|
||||||
|
|
||||||
|
export PYTHONPATH=${HOME}/src/picadae/control/app
|
||||||
|
python p_ac.py -c p_ac.yml
|
||||||
|
|
||||||
|
c 60 60 # caputre note 60 using the full_pulseL[] from the p_ac.yml
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
# plot third take of note 60
|
||||||
|
python plot_seq.py ~/temp/p_ac_3_oa/60 p_ac.yml 3
|
||||||
|
|
||||||
|
# record a calibration take: CalibrateKeys.start
|
||||||
|
uses cfg.full_pulseL[] for the pulse sequence
|
||||||
|
use cfg.calibrateArgs.holdDutyPctD{} for hold PWM duty cycle
|
||||||
|
|
||||||
|
# obsolete plotting function in calibrate_plot.py
|
||||||
|
python calibrate_plot.py ~/temp/p_ac_3_oa/60/2 p_ac.yml 60
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
![Plot Seq 1](doc/do_td_plot.png)
|
||||||
|
`python plot_seq.py ~/temp/p_ac_3_od p_ac.yml 60 10`
|
||||||
|
`do_td_plot(inDir,cfg.analysisArgs, pitch, take_id )`
|
||||||
|
|
||||||
|
![Multi Plot 1](doc/multi_plot.png)
|
||||||
|
`python plot_seq.py p_ac.yml ~/temp/p_ac_3_od td_multi_plot 60 3 60 4 60 5`
|
||||||
|
plot_seq.py `do_td_multi_plot(inDir,cfg.analysisArgs,[(36,4), (48,2)] )`
|
||||||
|
|
||||||
|
![Spectral Ranges](doc/plot_spectral_ranges.png)
|
||||||
|
`python plot_seq.py p_ac.yml ~/temp/p_ac_3_od plot_spectral_ranges 60 3 60 4`
|
||||||
|
|
||||||
|
|
||||||
|
![Multi Usec dB](doc/us_db.png)
|
||||||
|
`python plot_seq_1.py p_ac.yml ~/temp/p_ac_3_od us_db 84`
|
||||||
|
|
||||||
|
![Usec dB Spread](doc/us_db_map.png)
|
||||||
|
`python plot_seq_1.py p_ac.yml ~/temp/p_ac_3_od us_db_map 84 72`
|
||||||
|
|
||||||
|
![Min Max](doc/min_max_db.png)
|
||||||
|
`python plot_seq_1.py p_ac.yml ~/temp/p_ac_3_od min_max 36 48 60 72 84`
|
||||||
|
|
||||||
|
|
||||||
|
![Min Max 2](doc/min_max_db_2.png)
|
||||||
|
The last number in the list is the 'takeId'
|
||||||
|
`python plot_seq_1.py p_ac.yml ~/temp/p_ac_3_od min_max_2 36 48 60 72 84 2`
|
||||||
|
|
||||||
|
`python plot_seq_1.py p_ac.yml ~/temp/p_ac_3_od manual_db`
|
||||||
|
|
||||||
|
Interpolate across the min and max db values to form the min/max curves for the complete
|
||||||
|
set of keys. The anchor points for the curves are taken from cfg record
|
||||||
|
manuMinD,manualAnchorPitchMinDbL, and manualMaxDbL.
|
||||||
|
|
||||||
|
````
|
||||||
|
# select the event (takeId, eventIdx) to use to represent the min value for each pitch
|
||||||
|
manualMinD: {
|
||||||
|
36: [2, 10],
|
||||||
|
48: [2, 10],
|
||||||
|
60: [2, 10],
|
||||||
|
72: [2, 10],
|
||||||
|
84: [2, 10]
|
||||||
|
},
|
||||||
|
|
||||||
|
# leave 60 out of the min anchor point list
|
||||||
|
manualAnchorPitchMinDbL: [ 36,48,72,84 ],
|
||||||
|
|
||||||
|
manualAnchorPitchMaxDbL: [ 36,48,60,72,84 ],
|
||||||
|
|
||||||
|
````
|
BIN
do_td_plot.png
Normal file
After Width: | Height: | Size: 124 KiB |
BIN
doc/do_td_plot.png
Normal file
After Width: | Height: | Size: 170 KiB |
BIN
doc/manual_db.png
Normal file
After Width: | Height: | Size: 35 KiB |
BIN
doc/min_max_db_2.png
Normal file
After Width: | Height: | Size: 26 KiB |
BIN
doc/multi_plot.png
Normal file
After Width: | Height: | Size: 83 KiB |
BIN
doc/plot_spectral_ranges.png
Normal file
After Width: | Height: | Size: 56 KiB |
BIN
doc/us_db_map.png
Normal file
After Width: | Height: | Size: 46 KiB |
158
p_ac.yml
@ -3,8 +3,8 @@
|
|||||||
|
|
||||||
|
|
||||||
# Audio device setup
|
# Audio device setup
|
||||||
audio_off: {
|
audio: {
|
||||||
inPortLabel: "5 USB Audio CODEC:", #"HDA Intel PCH: CS4208", # "5 USB Audio CODEC:", #"5 USB Sound Device",
|
inPortLabel: "8 USB Audio CODEC:", #"HDA Intel PCH: CS4208", # "5 USB Audio CODEC:", #"5 USB Sound Device",
|
||||||
outPortLabel: ,
|
outPortLabel: ,
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -12,10 +12,10 @@
|
|||||||
inMonitorFl: False,
|
inMonitorFl: False,
|
||||||
outMonitorFl: False,
|
outMonitorFl: False,
|
||||||
throughFl: False,
|
throughFl: False,
|
||||||
#inPortLabel: "Fastlane:Fastlane MIDI A",
|
inPortLabel: "Fastlane:Fastlane MIDI A",
|
||||||
#outPortLabel: "Fastlane:Fastlane MIDI A"
|
outPortLabel: "Fastlane:Fastlane MIDI A"
|
||||||
inPortLabel: "picadae:picadae MIDI 1",
|
#inPortLabel: "picadae:picadae MIDI 1",
|
||||||
outPortLabel: "picadae:picadae MIDI 1"
|
#outPortLabel: "picadae:picadae MIDI 1"
|
||||||
},
|
},
|
||||||
|
|
||||||
# Picadae API args
|
# Picadae API args
|
||||||
@ -23,18 +23,19 @@
|
|||||||
serial_baud: 38400,
|
serial_baud: 38400,
|
||||||
i2c_base_addr: 21,
|
i2c_base_addr: 21,
|
||||||
prescaler_usec: 16,
|
prescaler_usec: 16,
|
||||||
|
pwm_div: 5,
|
||||||
serial_sync_timeout_ms: 10000,
|
serial_sync_timeout_ms: 10000,
|
||||||
|
|
||||||
|
|
||||||
# MeasureSeq args
|
# MeasureSeq args
|
||||||
outDir: "~/temp/p_ac_3g",
|
outDir: "~/temp/p_ac_3_oe",
|
||||||
noteDurMs: 500,
|
noteDurMs: 500,
|
||||||
pauseDurMs: 500,
|
pauseDurMs: 500,
|
||||||
reversePulseListFl: True,
|
reversePulseListFl: True,
|
||||||
useFullPulseListFl: True,
|
useFullPulseListFl: True,
|
||||||
maxSilentNoteCount: 4,
|
maxSilentNoteCount: 4,
|
||||||
silentNoteMaxPulseUs: 15000,
|
silentNoteMaxPulseUs: 15000,
|
||||||
silentNoteMinDurMs: 250,
|
silentNoteMinDurMs: 180, #250,
|
||||||
|
|
||||||
# Midi file player
|
# Midi file player
|
||||||
midiFileFn: "/home/kevin/media/audio/midi/txt/round4.txt",
|
midiFileFn: "/home/kevin/media/audio/midi/txt/round4.txt",
|
||||||
@ -57,15 +58,44 @@
|
|||||||
|
|
||||||
full_pulse8L: [ 10000, 10050, 10100, 10150, 10200, 10250, 10300, 10350, 10400, 10450, 10500, 10550, 10600, 10650, 10700, 10750, 10800, 10850, 10900, 10950, 11000, 11125, 11250, 11375, 11500, 11625, 11750, 11875, 12000, 12125, 12250, 12375, 12500, 12625, 12750, 12875, 13000, 13125, 13250, 13375, 13500, 13625, 13750, 13875, 14000, 14125, 14250, 14375, 14500, 14625, 14750, 14875, 15000, 15250, 15375, 15500, 15750, 16000, 16250, 16500, 16750, 17000, 17250, 17500, 17750, 18000, 18250, 18500, 18750, 19000, 19500, 20000, 20500, 21000, 21500, 22000, 22500, 23000, 23500, 24000, 24500, 25000, 25500, 26000, 26500, 27000, 27500, 28000, 28500, 29000, 30000, 31000, 32000, 33000, 34000, 35000, 36000, 37000, 38000, 39000, 40000 ],
|
full_pulse8L: [ 10000, 10050, 10100, 10150, 10200, 10250, 10300, 10350, 10400, 10450, 10500, 10550, 10600, 10650, 10700, 10750, 10800, 10850, 10900, 10950, 11000, 11125, 11250, 11375, 11500, 11625, 11750, 11875, 12000, 12125, 12250, 12375, 12500, 12625, 12750, 12875, 13000, 13125, 13250, 13375, 13500, 13625, 13750, 13875, 14000, 14125, 14250, 14375, 14500, 14625, 14750, 14875, 15000, 15250, 15375, 15500, 15750, 16000, 16250, 16500, 16750, 17000, 17250, 17500, 17750, 18000, 18250, 18500, 18750, 19000, 19500, 20000, 20500, 21000, 21500, 22000, 22500, 23000, 23500, 24000, 24500, 25000, 25500, 26000, 26500, 27000, 27500, 28000, 28500, 29000, 30000, 31000, 32000, 33000, 34000, 35000, 36000, 37000, 38000, 39000, 40000 ],
|
||||||
|
|
||||||
|
# full_pulse9L was the last pulse list used on Matt's piano
|
||||||
|
full_pulse9L: [11000, 11075, 11150, 11225, 11300, 11375, 11450, 11525, 11600,11675, 11750, 11825, 11900, 11975, 12050, 12125, 12200, 12275,12350, 12425, 12500, 12575, 12650, 12725, 12800, 12875, 12950, 13025, 13100, 13175, 13250, 13325, 13400, 13475, 13550, 13625, 13700, 13775, 13850, 13925, 14000, 14075, 14150, 14225, 14300, 14375, 14450, 14525, 14600, 14675, 14750, 14825, 14900, 14975],
|
||||||
|
|
||||||
full_pulseL: [11000, 11075, 11150, 11225, 11300, 11375, 11450, 11525, 11600,11675, 11750, 11825, 11900, 11975, 12050, 12125, 12200, 12275,12350, 12425, 12500, 12575, 12650, 12725, 12800, 12875, 12950, 13025, 13100, 13175, 13250, 13325, 13400, 13475, 13550, 13625, 13700, 13775, 13850, 13925, 14000, 14075, 14150, 14225, 14300, 14375, 14450, 14525, 14600, 14675, 14750, 14825, 14900, 14975],
|
|
||||||
|
|
||||||
full_pulse10L: [ 8750, 8800, 8850, 8900, 8950, 9000, 9050, 9100, 9150, 9200, 9250, 9300, 9350, 9400, 9450,9500, 9550, 9600, 9650, 9700, 9750, 9800, 9850, 9900, 9950, 10000, 10050, 10100, 10150, 10200, 10250, 10300, 10350, 10400, 10450, 10500, 10550, 10600, 10650, 10700, 10750, 10800, 10850, 10900, 10950, 11000, 11125, 11250, 11375, 11500, 11625, 11750, 11875, 12000, 12125, 12250, 12375, 12500, 12625, 12750, 12875, 13000, 13125, 13250, 13375, 13500, 13625, 13750, 13875, 14000, 14125, 14250, 14375, 14500, 14625, 14750, 14875, 15000, 15250, 15375, 15500, 15750, 16000, 16250, 16500, 16750, 17000, 17250, 17500, 17750, 18000, 18250, 18500, 18750, 19000, 19500, 20000, 20500, 21000, 21500, 22000, 22500, 23000, 23500, 24000, 24500, 25000, 25500, 26000, 26500, 27000, 27500, 28000, 28500, 29000, 30000, 31000, 32000, 33000, 34000, 35000, 36000, 37000, 38000, 39000, 40000 ],
|
full_pulse10L: [ 8750, 8800, 8850, 8900, 8950, 9000, 9050, 9100, 9150, 9200, 9250, 9300, 9350, 9400, 9450,9500, 9550, 9600, 9650, 9700, 9750, 9800, 9850, 9900, 9950, 10000, 10050, 10100, 10150, 10200, 10250, 10300, 10350, 10400, 10450, 10500, 10550, 10600, 10650, 10700, 10750, 10800, 10850, 10900, 10950, 11000, 11125, 11250, 11375, 11500, 11625, 11750, 11875, 12000, 12125, 12250, 12375, 12500, 12625, 12750, 12875, 13000, 13125, 13250, 13375, 13500, 13625, 13750, 13875, 14000, 14125, 14250, 14375, 14500, 14625, 14750, 14875, 15000, 15250, 15375, 15500, 15750, 16000, 16250, 16500, 16750, 17000, 17250, 17500, 17750, 18000, 18250, 18500, 18750, 19000, 19500, 20000, 20500, 21000, 21500, 22000, 22500, 23000, 23500, 24000, 24500, 25000, 25500, 26000, 26500, 27000, 27500, 28000, 28500, 29000, 30000, 31000, 32000, 33000, 34000, 35000, 36000, 37000, 38000, 39000, 40000 ],
|
||||||
|
|
||||||
full_pulse11L: [ 9000, 9050, 9100, 9150, 9200, 9250, 9300, 9350, 9400, 9450,9500, 9550, 9600, 9650, 9700, 9750, 9800, 9850, 9900, 9950, 10000, 10050, 10100, 10150, 10200, 10250, 10300, 10350, 10400, 10450, 10500, 10550, 10600, 10650, 10700, 10750, 10800, 10850, 10900, 10950, 11000, 11125, 11250, 11375, 11500, 11625, 11750, 11875, 12000, 12125, 12250, 12375, 12500, 12625, 12750, 12875, 13000, 13125, 13250, 13375, 13500, 13625, 13750, 13875, 14000, 14125, 14250, 14375, 14500, 14625, 14750, 14875, 15000 ],
|
full_pulse11L: [ 9000, 9050, 9100, 9150, 9200, 9250, 9300, 9350, 9400, 9450,9500, 9550, 9600, 9650, 9700, 9750, 9800, 9850, 9900, 9950, 10000, 10050, 10100, 10150, 10200, 10250, 10300, 10350, 10400, 10450, 10500, 10550, 10600, 10650, 10700, 10750, 10800, 10850, 10900, 10950, 11000, 11125, 11250, 11375, 11500, 11625, 11750, 11875, 12000, 12125, 12250, 12375, 12500, 12625, 12750, 12875, 13000, 13125, 13250, 13375, 13500, 13625, 13750, 13875, 14000, 14125, 14250, 14375, 14500, 14625, 14750, 14875, 15000 ],
|
||||||
|
|
||||||
full_pulse12L: [ 8750, 8800, 8850, 8900, 8950, 9000, 9050, 9100, 9150, 9200, 9250, 9300, 9350, 9400, 9450,9500, 9550, 9600, 9650, 9700, 9750, 9800, 9850, 9900, 9950, 10000, 10050, 10100, 10150, 10200, 10250, 10300, 10350, 10400, 10450, 10500, 10550, 10600, 10650, 10700, 10750, 10800, 10850, 10900, 10950, 11000, 11125, 11250, 11375, 11500, 11625, 11750, 11875, 12000, 12125, 12250, 12375, 12500, 12625, 12750, 12875, 13000, 13125, 13250, 13375, 13500, 13625, 13750, 13875, 14000, 14125, 14250, 14375, 14500, 14625, 14750, 14875, 15000 ],
|
full_pulse12L: [ 8750, 8800, 8850, 8900, 8950, 9000, 9050, 9100, 9150, 9200, 9250, 9300, 9350, 9400, 9450,9500, 9550, 9600, 9650, 9700, 9750, 9800, 9850, 9900, 9950, 10000, 10050, 10100, 10150, 10200, 10250, 10300, 10350, 10400, 10450, 10500, 10550, 10600, 10650, 10700, 10750, 10800, 10850, 10900, 10950, 11000, 11125, 11250, 11375, 11500, 11625, 11750, 11875, 12000, 12125, 12250, 12375, 12500, 12625, 12750, 12875, 13000, 13125, 13250, 13375, 13500, 13625, 13750, 13875, 14000, 14125, 14250, 14375, 14500, 14625, 14750, 14875, 15000 ],
|
||||||
|
|
||||||
|
# pulse lists below this line were developed on Steinway D (11/14/20)
|
||||||
|
# 60
|
||||||
|
full_pulse13L: [8000, 8250, 8500, 8750, 9000, 9250, 9500, 9750, 10000, 10250, 10500, 10750, 11000, 11250, 11500, 11750, 12000, 12250, 12500, 12750, 13000, 13500, 14000, 14500, 15000, 15500, 16000, 16500, 17000, 17500, 18000, 18500, 19000, 19500, 20000 ],
|
||||||
|
|
||||||
|
full_pulse14L: [ 9000, 9125, 9250, 9375, 9500, 9625, 9750, 9875, 10000, 10125, 10250, 10375, 10500, 10625, 10750, 10875, 11000, 11125, 11250, 11375, 11500, 11625, 11750, 11875, 12000, 12250, 12500, 12750, 13000, 13500, 14000, 14500, 15000, 15500, 16000, 16500, 17000, 17500, 18000, 18500, 19000, 19500, 20000, 20500, 21000, 21500, 22000 ],
|
||||||
|
|
||||||
|
full_pulse15L: [ 8000, 9000,10000, 11000, 12000, 13000, 14000, 15000, 16000, 17000, 18000, 19000, 20000 ],
|
||||||
|
|
||||||
|
full_pulse16L: [ 8000, 8125, 8250, 8375, 8500, 8625, 8750, 8875, 9000, 9125, 9250, 9375, 9500, 9625, 9750, 9875, 10000, 10125, 10250, 10375, 10500, 10625, 10750, 10875, 11000, 11125, 11250, 11375, 11500, 11625, 11750, 11875, 12000, 12250, 12500, 12750, 13000, 13500, 14000, 14500, 15000, 15500, 16000, 16500, 17000, 17500, 18000, 18500, 19000, 19500, 20000, 20500, 21000, 21500, 22000 ],
|
||||||
|
|
||||||
|
full_pulse17L: [ 8100, 8150, 8200, 8250, 8300, 8350, 8400, 8450, 8500, 8550, 8600, 8650, 8700, 8750, 8800, 8850, 8900, 8950, 9000, 9125, 9250, 9375, 9500, 9625, 9750, 9875, 10000, 10125, 10250, 10375, 10500, 10625, 10750, 10875, 11000, 11125, 11250, 11375, 11500, 11625, 11750, 11875, 12000, 12250, 12500, 12750, 13000, 13500, 14000, 14500, 15000, 15500, 16000, 16500, 17000, 17500, 18000, 18500, 19000, 19500, 20000, 20500, 21000, 21500, 22000 ],
|
||||||
|
|
||||||
|
full_pulse18L: [ 11500, 11625, 11750, 11875, 12000, 12250, 12500, 12750, 13000, 13500, 14000, 14500, 15000, 15500 ],
|
||||||
|
|
||||||
|
full_pulse19L: [ 11875, 12000, 12125, 12250, 12375, 12500, 12625, 12750, 12875, 13000, 13250, 13500, 13750, 14000, 14500, 15000, 15500, 16000, 16500 ],
|
||||||
|
|
||||||
|
# pulse lists below this line were developed on Steinway D w/ new firmware (11/21/20)
|
||||||
|
|
||||||
|
full_pulse20L: [ 1000, 1250, 1500, 1750, 2000, 2250, 2500, 2750, 3000, 3250, 3500, 3750, 4000, 4250, 4500, 4750, 5000, 5250, 5500, 5750, 6000, 6250, 6500, 6750, 7000, 7250, 7500, 7750, 8000, 8250, 8500, 8750, 9000, 9250, 9500, 9750, 10000, 10250, 10500, 10750, 11000, 11250, 11500, 11750, 12000, 12250, 12500, 12750, 13000, 13500, 14000, 14500, 15000, 15500 ],
|
||||||
|
|
||||||
|
# 60,72
|
||||||
|
full_pulse21L: [ 1000, 1250, 1500, 1750, 2000, 2250, 2500, 2750, 3000, 3250, 3500, 3750, 4000, 4500, 5000, 5500, 6000, 6500, 7000, 7500, 8000, 8500, 9000, 10000, 11000, 12000, 13000, 14000, 15000, 16000, 17000, 18000, 19000 ],
|
||||||
|
|
||||||
|
# 48,36,85
|
||||||
|
full_pulse22L: [ 3500, 3750, 4000, 4250, 4500, 4750, 5000, 5250, 5500, 5750, 6000, 6250, 6500, 6750, 7000, 7500, 8000, 8500, 9000, 9500, 10000, 10500, 11000, 12000, 12500, 13000, 14000, 15000, 16000, 17000, 18000, 19000 ],
|
||||||
|
|
||||||
|
# 84
|
||||||
|
full_pulseL: [ 2000, 2250, 2500,2750, 3000, 3250, 3500, 3750, 4000, 4250, 4500, 4750, 5000, 5250, 5500, 5750, 6000, 6250, 6500, 6750, 7000, 7500, 8000, 8500, 9000, 9500, 10000, 10500, 11000, 12000, 12500, 13000, 14000, 15000, 16000 ],
|
||||||
|
|
||||||
# RMS analysis args
|
# RMS analysis args
|
||||||
analysisArgs: {
|
analysisArgs: {
|
||||||
rmsAnalysisArgs: {
|
rmsAnalysisArgs: {
|
||||||
@ -77,11 +107,12 @@
|
|||||||
durDecayPct: 40, # percent drop in RMS to indicate the end of a note
|
durDecayPct: 40, # percent drop in RMS to indicate the end of a note
|
||||||
},
|
},
|
||||||
|
|
||||||
resampleMinDb: 7.0, # note's less than this will be skipped
|
resampleMinDb: -5.0, # note's less than this will be skipped
|
||||||
resampleNoiseLimitPct: 5.0, #
|
resampleNoiseLimitPct: 5.0, #
|
||||||
resampleMinDurMs: 800, # notes's whose duration is less than this will be skipped
|
resampleMinDurMs: 150, # notes's whose duration is less than this will be skipped
|
||||||
|
|
||||||
minAttkDb: 7.0, # threshold of silence level
|
useLastTakeOnlyFl: True,
|
||||||
|
minAttkDb: -5.0, # threshold of silence level
|
||||||
maxDbOffset: 0.25, # travel down the from the max. note level by at most this amount to locate the max. peak
|
maxDbOffset: 0.25, # travel down the from the max. note level by at most this amount to locate the max. peak
|
||||||
maxDeltaDb: 1.5, # maximum db change between volume samples (changes greater than this will trigger resampling)
|
maxDeltaDb: 1.5, # maximum db change between volume samples (changes greater than this will trigger resampling)
|
||||||
samplesPerDb: 4, # count of samples per dB to resample ranges whose range is less than maxDeltaDb
|
samplesPerDb: 4, # count of samples per dB to resample ranges whose range is less than maxDeltaDb
|
||||||
@ -93,6 +124,18 @@
|
|||||||
},
|
},
|
||||||
|
|
||||||
manualMinD: {
|
manualMinD: {
|
||||||
|
36: [2, 10],
|
||||||
|
48: [2, 10],
|
||||||
|
60: [2, 10],
|
||||||
|
72: [2, 10],
|
||||||
|
84: [2, 10]
|
||||||
|
},
|
||||||
|
|
||||||
|
manualAnchorPitchMinDbL: [ 36,48,72,84 ],
|
||||||
|
manualAnchorPitchMaxDbL: [ 36,48,60,72,84 ],
|
||||||
|
|
||||||
|
|
||||||
|
manualMinD_0: {
|
||||||
23: [2, 24],
|
23: [2, 24],
|
||||||
24: [2, 18],
|
24: [2, 18],
|
||||||
25: [2, 41],
|
25: [2, 41],
|
||||||
@ -175,8 +218,8 @@
|
|||||||
|
|
||||||
},
|
},
|
||||||
|
|
||||||
manualAnchorPitchMinDbL: [ 23, 27, 31, 34, 44, 51, 61, 70, 74, 81, 87, 93, 96, 101 ],
|
manualAnchorPitchMinDbL_0: [ 23, 27, 31, 34, 44, 51, 61, 70, 74, 81, 87, 93, 96, 101 ],
|
||||||
manualAnchorPitchMaxDbL: [ 23, 32, 49, 57, 67, 76, 83, 93, 99, 101 ],
|
manualAnchorPitchMaxDbL_0: [ 23, 32, 49, 57, 67, 76, 83, 93, 99, 101 ],
|
||||||
|
|
||||||
calibrateArgs: {
|
calibrateArgs: {
|
||||||
|
|
||||||
@ -213,6 +256,89 @@
|
|||||||
dbSrcLabel: 'hm', # source of the db measurement 'td' (time-domain) or 'hm' (harmonic)
|
dbSrcLabel: 'hm', # source of the db measurement 'td' (time-domain) or 'hm' (harmonic)
|
||||||
|
|
||||||
holdDutyPctD: {
|
holdDutyPctD: {
|
||||||
|
23: [[0, 40]],
|
||||||
|
24: [[0, 40]],
|
||||||
|
25: [[0, 40]],
|
||||||
|
26: [[0, 40]],
|
||||||
|
27: [[0, 40]],
|
||||||
|
28: [[0, 40]],
|
||||||
|
29: [[0, 40]],
|
||||||
|
30: [[0, 40]],
|
||||||
|
31: [[0, 40]],
|
||||||
|
32: [[0, 40]],
|
||||||
|
33: [[0, 40]],
|
||||||
|
34: [[0, 40]],
|
||||||
|
35: [[0, 40]],
|
||||||
|
36: [[0, 45]],
|
||||||
|
37: [[0, 40]],
|
||||||
|
38: [[0, 40]],
|
||||||
|
39: [[0, 40]],
|
||||||
|
40: [[0, 40]],
|
||||||
|
41: [[0, 40]],
|
||||||
|
42: [[0, 40]],
|
||||||
|
43: [[0, 40]],
|
||||||
|
44: [[0, 40]],
|
||||||
|
45: [[0, 40]],
|
||||||
|
46: [[0, 40]],
|
||||||
|
47: [[0, 40]],
|
||||||
|
48: [[0, 40]],
|
||||||
|
49: [[0, 40]],
|
||||||
|
50: [[0, 40]],
|
||||||
|
51: [[0, 40]],
|
||||||
|
52: [[0, 40]],
|
||||||
|
53: [[0, 40]],
|
||||||
|
54: [[0, 40]],
|
||||||
|
55: [[0, 40]],
|
||||||
|
56: [[0, 40]],
|
||||||
|
57: [[0, 40]],
|
||||||
|
58: [[0, 40]],
|
||||||
|
59: [[0, 40]],
|
||||||
|
60: [[0, 50]],
|
||||||
|
61: [[0, 43]],
|
||||||
|
62: [[0, 43]],
|
||||||
|
63: [[0, 43]],
|
||||||
|
64: [[0, 40],[14000, 45],[15000,50],[16000,55],[17000,60],[18000,65],[19000,55]],
|
||||||
|
65: [[0, 99]],
|
||||||
|
66: [[0, 40]],
|
||||||
|
67: [[0, 40],[14000, 45],[15000,50],[16000,55],[17000,60],[18000,65],[19000,55]],
|
||||||
|
68: [[0, 40],[14000, 45],[15000,50],[16000,55],[17000,60],[18000,65],[19000,55]],
|
||||||
|
69: [[0, 40],[14000, 45],[15000,50],[16000,55],[17000,60],[18000,65],[19000,55]],
|
||||||
|
70: [[0, 40],[14000, 45],[15000,50],[16000,55],[17000,60],[18000,65],[19000,55]],
|
||||||
|
71: [[0, 40],[14000, 45],[15000,50],[16000,55],[17000,60],[18000,65],[19000,55]],
|
||||||
|
72: [[0, 45],[11000, 65] ],
|
||||||
|
73: [[0, 45],[14000,45],[15000,50],[16000,55],[17000,60],[18000,65],[19000,55]],
|
||||||
|
74: [[0, 40],[14000,45],[15000,50],[16000,55],[17000,60],[18000,65],[19000,55]],
|
||||||
|
75: [[0, 40],[14000,45],[15000,50],[16000,55],[17000,60],[18000,65],[19000,55]],
|
||||||
|
76: [[0, 40],[14000,45],[15000,50],[16000,55],[17000,60],[18000,65],[19000,55]],
|
||||||
|
77: [[0, 40],[14000,45],[15000,50],[16000,55],[17000,60],[18000,65],[19000,55]],
|
||||||
|
78: [[0, 40],[14000,45],[15000,50],[16000,55],[17000,60],[18000,65],[19000,55]],
|
||||||
|
79: [[0, 40],[14000,45],[15000,50],[16000,55],[17000,60],[18000,65],[19000,55]],
|
||||||
|
80: [[0, 40],[14000,45],[15000,50],[16000,55],[17000,60],[18000,65],[19000,55]],
|
||||||
|
81: [[0, 40],[14000,45],[15000,50],[16000,55],[17000,60],[18000,65],[19000,55]],
|
||||||
|
82: [[0, 40],[14000,45],[15000,50],[16000,55],[17000,60],[18000,65],[19000,55]],
|
||||||
|
83: [[0, 42]],
|
||||||
|
84: [[0, 42],[10000,50],[11000,60]],
|
||||||
|
85: [[0, 42],[9000,45],[10000,50],[11000,60]],
|
||||||
|
86: [[0, 40]],
|
||||||
|
87: [[0, 40]],
|
||||||
|
88: [[0, 40]],
|
||||||
|
89: [[0, 40]],
|
||||||
|
91: [[0, 40]],
|
||||||
|
92: [[0, 40]],
|
||||||
|
93: [[0, 40]],
|
||||||
|
94: [[0, 40]],
|
||||||
|
95: [[0, 40]],
|
||||||
|
96: [[0, 40]],
|
||||||
|
97: [[0, 40]],
|
||||||
|
98: [[0, 40]],
|
||||||
|
99: [[0, 40]],
|
||||||
|
100: [[0, 40]],
|
||||||
|
101: [[0, 40]],
|
||||||
|
106: [[0, 40]]
|
||||||
|
},
|
||||||
|
|
||||||
|
# Final for Matt's piano
|
||||||
|
holdDutyPct0D: {
|
||||||
23: [[0, 70]],
|
23: [[0, 70]],
|
||||||
24: [[0, 75]],
|
24: [[0, 75]],
|
||||||
25: [[0, 70]],
|
25: [[0, 70]],
|
||||||
|
202
plot_seq.py
@ -1,14 +1,22 @@
|
|||||||
##| Copyright: (C) 2019-2020 Kevin Larke <contact AT larke DOT org>
|
##| Copyright: (C) 2019-2020 Kevin Larke <contact AT larke DOT org>
|
||||||
##| License: GNU GPL version 3.0 or above. See the accompanying LICENSE file.
|
##| License: GNU GPL version 3.0 or above. See the accompanying LICENSE file.
|
||||||
import os, sys
|
import os, sys,json
|
||||||
import matplotlib.pyplot as plt
|
import matplotlib.pyplot as plt
|
||||||
import numpy as np
|
import numpy as np
|
||||||
|
from scipy.io import wavfile
|
||||||
from common import parse_yaml_cfg
|
from common import parse_yaml_cfg
|
||||||
from rms_analysis import rms_analysis_main
|
from rms_analysis import rms_analysis_main
|
||||||
from rms_analysis import select_first_stable_note_by_delta_db
|
from rms_analysis import select_first_stable_note_by_delta_db
|
||||||
from rms_analysis import select_first_stable_note_by_dur
|
from rms_analysis import select_first_stable_note_by_dur
|
||||||
from rms_analysis import samples_to_linear_residual
|
from rms_analysis import samples_to_linear_residual
|
||||||
|
|
||||||
|
import rms_analysis as ra
|
||||||
|
|
||||||
|
#from rms_analysis import audio_rms
|
||||||
|
#from rms_analysis import locate_peak_indexes
|
||||||
|
#from rms_analysis import audio_stft_rms
|
||||||
|
#from rms_analysis import calc_harm_bins
|
||||||
|
|
||||||
def is_nanV( xV ):
|
def is_nanV( xV ):
|
||||||
|
|
||||||
for i in range(xV.shape[0]):
|
for i in range(xV.shape[0]):
|
||||||
@ -31,13 +39,16 @@ def _find_max_take_id( inDir ):
|
|||||||
|
|
||||||
def form_final_pulse_list( inDir, midi_pitch, analysisArgsD, take_id=None ):
|
def form_final_pulse_list( inDir, midi_pitch, analysisArgsD, take_id=None ):
|
||||||
|
|
||||||
|
|
||||||
# append the midi pitch to the input directory
|
# append the midi pitch to the input directory
|
||||||
#inDir = os.path.join( inDir, "%i" % (midi_pitch))
|
inDir = os.path.join( inDir, str(midi_pitch))
|
||||||
|
|
||||||
dirL = os.listdir(inDir)
|
dirL = os.listdir(inDir)
|
||||||
|
|
||||||
pkL = []
|
pkL = []
|
||||||
|
|
||||||
|
maxTakeNumber = 0
|
||||||
|
|
||||||
# for each take in this directory
|
# for each take in this directory
|
||||||
for idir in dirL:
|
for idir in dirL:
|
||||||
|
|
||||||
@ -46,10 +57,18 @@ def form_final_pulse_list( inDir, midi_pitch, analysisArgsD, take_id=None ):
|
|||||||
|
|
||||||
if not os.path.isfile(os.path.join( inDir,idir, "seq.json")):
|
if not os.path.isfile(os.path.join( inDir,idir, "seq.json")):
|
||||||
continue
|
continue
|
||||||
|
|
||||||
|
if analysisArgsD['useLastTakeOnlyFl']:
|
||||||
|
if take_number > maxTakeNumber:
|
||||||
|
pkL.clear()
|
||||||
|
maxTakeNumber = take_number
|
||||||
|
else:
|
||||||
|
continue
|
||||||
|
|
||||||
|
|
||||||
# analyze this takes audio and locate the note peaks
|
# analyze this takes audio and locate the note peaks
|
||||||
r = rms_analysis_main( os.path.join(inDir,idir), midi_pitch, **analysisArgsD['rmsAnalysisArgs'] )
|
r = rms_analysis_main( os.path.join(inDir,idir), midi_pitch, **analysisArgsD['rmsAnalysisArgs'] )
|
||||||
|
|
||||||
# store the peaks in pkL[ (db,us) ]
|
# store the peaks in pkL[ (db,us) ]
|
||||||
for db,us in zip(r.pkDbL,r.pkUsL):
|
for db,us in zip(r.pkDbL,r.pkUsL):
|
||||||
pkL.append( (db,us) )
|
pkL.append( (db,us) )
|
||||||
@ -68,6 +87,8 @@ def form_final_pulse_list( inDir, midi_pitch, analysisArgsD, take_id=None ):
|
|||||||
# locate the first and last note
|
# locate the first and last note
|
||||||
min_pk_idx, max_pk_idx = find_min_max_peak_index( pkDbL, analysisArgsD['minAttkDb'], analysisArgsD['maxDbOffset'] )
|
min_pk_idx, max_pk_idx = find_min_max_peak_index( pkDbL, analysisArgsD['minAttkDb'], analysisArgsD['maxDbOffset'] )
|
||||||
|
|
||||||
|
print("MIN MAX:",min_pk_idx,pkUsL[min_pk_idx],max_pk_idx,pkUsL[max_pk_idx])
|
||||||
|
|
||||||
db1 = pkDbL[ max_pk_idx ]
|
db1 = pkDbL[ max_pk_idx ]
|
||||||
db0 = pkDbL[ min_pk_idx ]
|
db0 = pkDbL[ min_pk_idx ]
|
||||||
|
|
||||||
@ -195,14 +216,12 @@ def calc_resample_ranges( pkDbL, pkUsL, min_pk_idx, max_pk_idx, maxDeltaDb, samp
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
def form_resample_pulse_time_list( inDir, analysisArgsD ):
|
def form_resample_pulse_time_list( inDir, midi_pitch, analysisArgsD ):
|
||||||
"""" This function merges all available data from previous takes to form
|
"""" This function merges all available data from previous takes to form
|
||||||
a new list of pulse times to sample.
|
a new list of pulse times to sample.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
# the last folder is always the midi pitch of the note under analysis
|
inDir = os.path.join( inDir, str(midi_pitch) )
|
||||||
midi_pitch = int( inDir.split("/")[-1] )
|
|
||||||
|
|
||||||
dirL = os.listdir(inDir)
|
dirL = os.listdir(inDir)
|
||||||
|
|
||||||
pkL = []
|
pkL = []
|
||||||
@ -248,11 +267,10 @@ def plot_curve( ax, pulseUsL, rmsDbV ):
|
|||||||
|
|
||||||
ax.plot( pulseUsL, func(pulseUsL), color='red')
|
ax.plot( pulseUsL, func(pulseUsL), color='red')
|
||||||
|
|
||||||
def plot_resample_pulse_times_0( inDir, analysisArgsD ):
|
def plot_resample_pulse_times_0( inDir, analysisArgsD, midi_pitch, printDir="" ):
|
||||||
|
|
||||||
newPulseUsL, rmsDbV, pulseUsL = form_resample_pulse_time_list( inDir, analysisArgsD )
|
newPulseUsL, rmsDbV, pulseUsL = form_resample_pulse_time_list( inDir, midi_pitch, analysisArgsD )
|
||||||
|
|
||||||
midi_pitch = int( inDir.split("/")[-1] )
|
|
||||||
velTblUsL,velTblDbL,_ = form_final_pulse_list( inDir, midi_pitch, analysisArgsD, take_id=None )
|
velTblUsL,velTblDbL,_ = form_final_pulse_list( inDir, midi_pitch, analysisArgsD, take_id=None )
|
||||||
|
|
||||||
fig,ax = plt.subplots()
|
fig,ax = plt.subplots()
|
||||||
@ -262,16 +280,18 @@ def plot_resample_pulse_times_0( inDir, analysisArgsD ):
|
|||||||
for us in newPulseUsL:
|
for us in newPulseUsL:
|
||||||
ax.axvline( x = us )
|
ax.axvline( x = us )
|
||||||
|
|
||||||
ax.plot(velTblUsL,velTblDbL,marker='.',linestyle='None')
|
print(len(velTblUsL))
|
||||||
|
ax.plot(velTblUsL,velTblDbL,marker='.',linestyle='None',color='red')
|
||||||
|
|
||||||
|
if printDir:
|
||||||
|
plt.savefig(os.path.join(printDir,"plot_resample_times_0.png"),format="png")
|
||||||
|
|
||||||
plt.show()
|
plt.show()
|
||||||
|
|
||||||
def plot_resample_pulse_times( inDir, analysisArgsD ):
|
def plot_resample_pulse_times( inDir, analysisArgsD, midi_pitch, printDir="" ):
|
||||||
|
|
||||||
newPulseUsL, rmsDbV, pulseUsL = form_resample_pulse_time_list( inDir, analysisArgsD )
|
newPulseUsL, rmsDbV, pulseUsL = form_resample_pulse_time_list( inDir, midi_pitch, analysisArgsD )
|
||||||
|
|
||||||
midi_pitch = int( inDir.split("/")[-1] )
|
|
||||||
velTblUsL,velTblDbL,_ = form_final_pulse_list( inDir, midi_pitch, analysisArgsD, take_id=None )
|
velTblUsL,velTblDbL,_ = form_final_pulse_list( inDir, midi_pitch, analysisArgsD, take_id=None )
|
||||||
|
|
||||||
fig,axL = plt.subplots(2,1,gridspec_kw={'height_ratios': [2, 1]})
|
fig,axL = plt.subplots(2,1,gridspec_kw={'height_ratios': [2, 1]})
|
||||||
@ -290,6 +310,10 @@ def plot_resample_pulse_times( inDir, analysisArgsD ):
|
|||||||
axL[1].axhline(1.0,color='black')
|
axL[1].axhline(1.0,color='black')
|
||||||
axL[1].plot(pulseUsL,np.abs(scoreV * 100.0 / rmsDbV))
|
axL[1].plot(pulseUsL,np.abs(scoreV * 100.0 / rmsDbV))
|
||||||
axL[1].set_ylim((0.0,50))
|
axL[1].set_ylim((0.0,50))
|
||||||
|
|
||||||
|
if printDir:
|
||||||
|
plt.savefig(os.path.join(printDir,"plot_resample_times.png"),format="png")
|
||||||
|
|
||||||
plt.show()
|
plt.show()
|
||||||
|
|
||||||
|
|
||||||
@ -369,7 +393,7 @@ def plot_spectrum( ax, srate, binHz, specV, midiPitch, harmN ):
|
|||||||
""" Plot a single spectrum, 'specV' and the harmonic peak location boundaries."""
|
""" Plot a single spectrum, 'specV' and the harmonic peak location boundaries."""
|
||||||
|
|
||||||
binN = specV.shape[0]
|
binN = specV.shape[0]
|
||||||
harmLBinL,harmMBinL,harmUBinL = calc_harm_bins( srate, binHz, midiPitch, harmN )
|
harmLBinL,harmMBinL,harmUBinL = ra.calc_harm_bins( srate, binHz, midiPitch, harmN )
|
||||||
|
|
||||||
fundHz = harmMBinL[0] * binHz
|
fundHz = harmMBinL[0] * binHz
|
||||||
maxPlotHz = fundHz * (harmN+1)
|
maxPlotHz = fundHz * (harmN+1)
|
||||||
@ -386,33 +410,40 @@ def plot_spectrum( ax, srate, binHz, specV, midiPitch, harmN ):
|
|||||||
ax.axvline( x=h1 * binHz, color="black")
|
ax.axvline( x=h1 * binHz, color="black")
|
||||||
ax.axvline( x=h2 * binHz, color="blue")
|
ax.axvline( x=h2 * binHz, color="blue")
|
||||||
|
|
||||||
ax.set_ylabel(str(midiPitch))
|
ax.set_ylabel("dB : %i " % (midiPitch))
|
||||||
|
|
||||||
def plot_spectral_ranges( inDir, pitchL, rmsWndMs=300, rmsHopMs=30, harmN=5, dbLinRef=0.001 ):
|
def plot_spectral_ranges( inDir, pitchTakeL, rmsWndMs=300, rmsHopMs=30, harmN=5, dbLinRef=0.001, printDir="" ):
|
||||||
""" Plot the spectrum from one note (7th from last) in each attack pulse length sequence referred to by pitchL."""
|
""" Plot the spectrum from one note (7th from last) in each attack pulse length sequence referred to by pitchTakeL."""
|
||||||
|
|
||||||
plotN = len(pitchL)
|
plotN = len(pitchTakeL)
|
||||||
fig,axL = plt.subplots(plotN,1)
|
fig,axL = plt.subplots(plotN,1)
|
||||||
|
|
||||||
for plot_idx,midiPitch in enumerate(pitchL):
|
for plot_idx,(midiPitch,takeId) in enumerate(pitchTakeL):
|
||||||
|
|
||||||
# get the audio and meta-data file names
|
# get the audio and meta-data file names
|
||||||
seqFn = os.path.join( inDir, str(midiPitch), "seq.json")
|
seqFn = os.path.join( inDir, str(midiPitch), str(takeId), "seq.json")
|
||||||
audioFn = os.path.join( inDir, str(midiPitch), "audio.wav")
|
audioFn = os.path.join( inDir, str(midiPitch), str(takeId), "audio.wav")
|
||||||
|
|
||||||
# read the meta data object
|
# read the meta data object
|
||||||
with open( seqFn, "rb") as f:
|
with open( seqFn, "rb") as f:
|
||||||
r = json.load(f)
|
r = json.load(f)
|
||||||
|
|
||||||
# read the audio file
|
# read the audio file
|
||||||
srate, signalM = wavfile.read(audioFn)
|
srate, signalM = wavfile.read(audioFn)
|
||||||
|
|
||||||
|
|
||||||
|
# convert the audio signal vector to contain only the first (left) channel
|
||||||
|
if len(signalM.shape)>1:
|
||||||
|
signalM = signalM[:,0].squeeze()
|
||||||
|
|
||||||
sigV = signalM / float(0x7fff)
|
sigV = signalM / float(0x7fff)
|
||||||
|
|
||||||
|
|
||||||
# calc. the RMS envelope in the time domain
|
# calc. the RMS envelope in the time domain
|
||||||
rms0DbV, rms0_srate = audio_rms( srate, sigV, rmsWndMs, rmsHopMs, dbLinRef )
|
rms0DbV, rms0_srate = ra.audio_rms( srate, sigV, rmsWndMs, rmsHopMs, dbLinRef )
|
||||||
|
|
||||||
# locate the sample index of the peak of each note attack
|
# locate the sample index of the peak of each note attack
|
||||||
pkIdx0L = locate_peak_indexes( rms0DbV, rms0_srate, r['eventTimeL'] )
|
pkIdx0L = ra.locate_peak_indexes( rms0DbV, rms0_srate, r['eventTimeL'] )
|
||||||
|
|
||||||
# select the 7th to last note for spectrum measurement
|
# select the 7th to last note for spectrum measurement
|
||||||
|
|
||||||
@ -423,33 +454,47 @@ def plot_spectral_ranges( inDir, pitchL, rmsWndMs=300, rmsHopMs=30, harmN=5, dbL
|
|||||||
|
|
||||||
|
|
||||||
# calc. the RMS envelope by taking the max spectral peak in each STFT window
|
# calc. the RMS envelope by taking the max spectral peak in each STFT window
|
||||||
rmsDbV, rms_srate, specV, specHopIdx, binHz = audio_stft_rms( srate, sigV, rmsWndMs, rmsHopMs, dbLinRef, spectrumSmpIdx)
|
rmsDbV, rms_srate, specV, specHopIdx, binHz = ra.audio_stft_rms( srate, sigV, rmsWndMs, rmsHopMs, dbLinRef, spectrumSmpIdx)
|
||||||
|
|
||||||
# specV[] is the spectrum of the note at spectrumSmpIdx
|
# specV[] is the spectrum of the note at spectrumSmpIdx
|
||||||
|
|
||||||
# plot the spectrum and the harmonic selection ranges
|
# plot the spectrum and the harmonic selection ranges
|
||||||
plot_spectrum( axL[plot_idx], srate, binHz, specV, midiPitch, harmN )
|
plot_spectrum( axL[plot_idx], srate, binHz, specV, midiPitch, harmN )
|
||||||
|
|
||||||
|
axL[-1].set_xlabel("Hertz")
|
||||||
|
|
||||||
|
if printDir:
|
||||||
|
plt.savefig(os.path.join(printDir,"plot_spectral_ranges.png"),format="png")
|
||||||
|
|
||||||
plt.show()
|
plt.show()
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def td_plot( ax, inDir, midi_pitch, id, analysisArgsD ):
|
def td_plot( ax, inDir, midi_pitch, id, analysisArgsD ):
|
||||||
|
|
||||||
|
#
|
||||||
r = rms_analysis_main( inDir, midi_pitch, **analysisArgsD['rmsAnalysisArgs'] )
|
r = rms_analysis_main( inDir, midi_pitch, **analysisArgsD['rmsAnalysisArgs'] )
|
||||||
|
|
||||||
|
# find min/max peak in the sequence
|
||||||
min_pk_idx, max_pk_idx = find_min_max_peak_index( r.pkDbL, analysisArgsD['minAttkDb'], analysisArgsD['maxDbOffset'] )
|
min_pk_idx, max_pk_idx = find_min_max_peak_index( r.pkDbL, analysisArgsD['minAttkDb'], analysisArgsD['maxDbOffset'] )
|
||||||
|
|
||||||
|
# find ranges of the sequence which should be skipped because they are noisy or unreliable
|
||||||
skipPkIdxL = find_skip_peaks( r.rmsDbV, r.pkIdxL, min_pk_idx, max_pk_idx )
|
skipPkIdxL = find_skip_peaks( r.rmsDbV, r.pkIdxL, min_pk_idx, max_pk_idx )
|
||||||
|
|
||||||
|
# find peaks whose difference to surrounding peaks is greater than 'maxDeltaDb'.
|
||||||
jmpPkIdxL = find_out_of_range_peaks( r.rmsDbV, r.pkIdxL, min_pk_idx, max_pk_idx, analysisArgsD['maxDeltaDb'] )
|
jmpPkIdxL = find_out_of_range_peaks( r.rmsDbV, r.pkIdxL, min_pk_idx, max_pk_idx, analysisArgsD['maxDeltaDb'] )
|
||||||
|
|
||||||
secV = np.arange(0,len(r.rmsDbV)) / r.rms_srate
|
|
||||||
|
|
||||||
ax.plot( secV, r.rmsDbV )
|
secV = np.arange(0,len(r.rmsDbV)) / r.rms_srate
|
||||||
ax.plot( np.arange(0,len(r.tdRmsDbV)) / r.rms_srate, r.tdRmsDbV, color="black" )
|
|
||||||
|
# plot the harmonic RMS signal
|
||||||
|
ax.plot( secV, r.rmsDbV, color='blue', label="Harmonic" )
|
||||||
|
|
||||||
|
# plot the time-domain RMS signal
|
||||||
|
ax.plot( np.arange(0,len(r.tdRmsDbV)) / r.rms_srate, r.tdRmsDbV, color="black", label="TD" )
|
||||||
|
|
||||||
|
|
||||||
# print beg/end boundaries
|
# print note beg/end/peak boundaries
|
||||||
for i,(begMs, endMs) in enumerate(r.eventTimeL):
|
for i,(begMs, endMs) in enumerate(r.eventTimeL):
|
||||||
|
|
||||||
pkSec = r.pkIdxL[i] / r.rms_srate
|
pkSec = r.pkIdxL[i] / r.rms_srate
|
||||||
@ -472,38 +517,45 @@ def td_plot( ax, inDir, midi_pitch, id, analysisArgsD ):
|
|||||||
ax.plot( [pki / r.rms_srate], [ r.rmsDbV[pki] ], marker=6, color="blue")
|
ax.plot( [pki / r.rms_srate], [ r.rmsDbV[pki] ], marker=6, color="blue")
|
||||||
|
|
||||||
|
|
||||||
|
ax.legend();
|
||||||
|
ax.set_ylabel("dB");
|
||||||
|
|
||||||
return r
|
return r
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def do_td_plot( inDir, analysisArgs ):
|
def do_td_plot( inDir, analysisArgs, midi_pitch, takeId, printDir="" ):
|
||||||
|
|
||||||
fig,axL = plt.subplots(3,1)
|
fig,axL = plt.subplots(3,1)
|
||||||
fig.set_size_inches(18.5, 10.5, forward=True)
|
fig.set_size_inches(18.5, 10.5, forward=True)
|
||||||
|
|
||||||
id = int(inDir.split("/")[-1])
|
# parse the file name
|
||||||
midi_pitch = int(inDir.split("/")[-2])
|
inDir = os.path.join(inDir,str(midi_pitch),str(takeId));
|
||||||
|
|
||||||
r = td_plot(axL[0],inDir,midi_pitch,id,analysisArgs)
|
# plot the time domain signal
|
||||||
|
r = td_plot(axL[0],inDir,midi_pitch,takeId,analysisArgs)
|
||||||
|
|
||||||
qualityV = np.array([ x.quality for x in r.statsL ]) * np.max(r.pkDbL)
|
qualityV = np.array([ x.quality for x in r.statsL ]) * np.max(r.pkDbL)
|
||||||
durMsV = np.array([ x.durMs for x in r.statsL ])
|
durMsV = np.array([ x.durMs for x in r.statsL ])
|
||||||
avgV = np.array([ x.durAvgDb for x in r.statsL ])
|
avgV = np.array([ x.durAvgDb for x in r.statsL ])
|
||||||
|
|
||||||
#durMsV[ durMsV < 400 ] = 0
|
|
||||||
#durMsV = durMsV * np.max(r.pkDbL)/np.max(durMsV)
|
|
||||||
#durMsV = durMsV / 100.0
|
|
||||||
|
|
||||||
dV = np.diff(r.pkDbL) / r.pkDbL[1:]
|
dV = np.diff(r.pkDbL) / r.pkDbL[1:]
|
||||||
|
|
||||||
axL[1].plot( r.pkUsL, r.pkDbL, marker='.',label="pkDb" )
|
axL[1].plot( r.pkUsL, r.pkDbL, marker='.', label="harmonic" )
|
||||||
axL[1].plot( r.pkUsL, qualityV, marker='.',label="quality" )
|
#axL[1].plot( r.pkUsL, qualityV, marker='.', label="quality" )
|
||||||
axL[1].plot( r.pkUsL, avgV, marker='.',label="avgDb" )
|
axL[1].plot( r.pkUsL, avgV, marker='.', label="harm-td avg" )
|
||||||
#axL[2].plot( r.pkUsL, durMsV, marker='.' )
|
|
||||||
axL[2].plot( r.pkUsL[1:], dV, marker='.',label='d')
|
|
||||||
axL[2].set_ylim([-1,1])
|
|
||||||
axL[1].legend()
|
axL[1].legend()
|
||||||
|
axL[1].set_ylabel("dB");
|
||||||
|
|
||||||
|
#axL[2].plot( r.pkUsL, durMsV, marker='.' )
|
||||||
|
axL[2].plot( r.pkUsL[1:], dV, marker='.',label='delta')
|
||||||
|
axL[2].set_ylim([-1,1])
|
||||||
|
axL[2].legend()
|
||||||
|
axL[2].set_ylabel("dB");
|
||||||
|
|
||||||
|
axL[2].set_xlabel("Microseconds")
|
||||||
|
|
||||||
|
|
||||||
sni = select_first_stable_note_by_dur( durMsV )
|
sni = select_first_stable_note_by_dur( durMsV )
|
||||||
if sni is not None:
|
if sni is not None:
|
||||||
@ -519,41 +571,63 @@ def do_td_plot( inDir, analysisArgs ):
|
|||||||
|
|
||||||
for i in range(1,len(r.pkUsL)):
|
for i in range(1,len(r.pkUsL)):
|
||||||
axL[2].text( r.pkUsL[i], dV[i-1], "%i" % (i))
|
axL[2].text( r.pkUsL[i], dV[i-1], "%i" % (i))
|
||||||
|
|
||||||
|
if printDir:
|
||||||
|
plt.savefig(os.path.join(printDir,"do_td_plot.png"),format="png")
|
||||||
plt.show()
|
plt.show()
|
||||||
|
|
||||||
def do_td_multi_plot( inDir, analysisArgs ):
|
def do_td_multi_plot( inDir, analysisArgs, pitchTakeL, printPlotFl=False ):
|
||||||
|
|
||||||
midi_pitch = int(inDir.split("/")[-1])
|
#midi_pitch = int(inDir.split("/")[-1])
|
||||||
|
|
||||||
dirL = os.listdir(inDir)
|
fig,axL = plt.subplots(len(pitchTakeL),1)
|
||||||
|
|
||||||
fig,axL = plt.subplots(len(dirL),1)
|
|
||||||
|
|
||||||
|
|
||||||
for id,(idir,ax) in enumerate(zip(dirL,axL)):
|
for id,((pitch,takeId),ax) in enumerate(zip(pitchTakeL,axL)):
|
||||||
|
|
||||||
td_plot(ax, os.path.join(inDir,str(id)), midi_pitch, id, analysisArgs )
|
td_plot(ax, os.path.join(inDir,str(pitch),str(takeId)), pitch, takeId, analysisArgs )
|
||||||
|
|
||||||
|
ax.set_xlabel("Seconds")
|
||||||
|
|
||||||
|
if printDir:
|
||||||
|
plt.savefig(os.path.join(printDir,"multi_plot.png"),format="png")
|
||||||
plt.show()
|
plt.show()
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
|
|
||||||
inDir = sys.argv[1]
|
printDir = os.path.expanduser("~/src/picadae_ac_3/doc")
|
||||||
cfgFn = sys.argv[2]
|
cfgFn = sys.argv[1]
|
||||||
take_id = None if len(sys.argv)<4 else sys.argv[3]
|
inDir = sys.argv[2]
|
||||||
|
mode = sys.argv[3]
|
||||||
|
|
||||||
cfg = parse_yaml_cfg( cfgFn )
|
cfg = parse_yaml_cfg( cfgFn )
|
||||||
|
|
||||||
|
if mode == "td_plot":
|
||||||
|
|
||||||
|
# python plot_seq.py p_ac.yml ~/temp/p_ac_3_od td_plot 60 10
|
||||||
|
pitch = int(sys.argv[4])
|
||||||
|
take_id = int(sys.argv[5])
|
||||||
|
do_td_plot(inDir,cfg.analysisArgs, pitch, take_id, printDir )
|
||||||
|
|
||||||
if take_id is not None:
|
elif mode == "td_multi_plot" or mode == 'plot_spectral_ranges':
|
||||||
inDir = os.path.join(inDir,take_id)
|
|
||||||
do_td_plot(inDir,cfg.analysisArgs)
|
|
||||||
else:
|
|
||||||
#do_td_multi_plot(inDir,cfg.analysisArgs)
|
|
||||||
|
|
||||||
#plot_spectral_ranges( inDir, [ 24, 36, 48, 60, 72, 84, 96, 104] )
|
pitchTakeIdL = []
|
||||||
|
for i in range(4,len(sys.argv),2):
|
||||||
|
pitchTakeIdL.append( (int(sys.argv[i]), int(sys.argv[i+1])) )
|
||||||
|
|
||||||
plot_resample_pulse_times( inDir, cfg.analysisArgs )
|
if mode == "td_multi_plot":
|
||||||
|
# python plot_seq.py p_ac.yml ~/temp/p_ac_3_od td_multi_plot 36 2 48 3 60 4
|
||||||
|
do_td_multi_plot(inDir, cfg.analysisArgs, pitchTakeIdL, printDir )
|
||||||
|
else:
|
||||||
|
# python plot_seq.py p_ac.yml ~/temp/p_ac_3_od plot_spectral_ranges 36 2 48 3 60 4
|
||||||
|
plot_spectral_ranges( inDir, pitchTakeIdL, printDir=printDir )
|
||||||
|
|
||||||
|
elif mode == "resample_pulse_times":
|
||||||
|
|
||||||
|
# python plot_seq.py p_ac.yml ~/temp/p_ac_3_od resample_pulse_times 60
|
||||||
|
pitch = int(sys.argv[4])
|
||||||
|
plot_resample_pulse_times( inDir, cfg.analysisArgs, pitch, printDir )
|
||||||
|
|
||||||
|
else:
|
||||||
|
print("Unknown plot mode:%s" % (mode))
|
||||||
|
164
plot_seq_1.py
@ -14,8 +14,9 @@ def fit_to_reference( pkL, refTakeId ):
|
|||||||
dur_outL = []
|
dur_outL = []
|
||||||
tid_outL = []
|
tid_outL = []
|
||||||
|
|
||||||
|
|
||||||
dbL,usL,durMsL,takeIdL = tuple(zip(*pkL))
|
dbL,usL,durMsL,takeIdL = tuple(zip(*pkL))
|
||||||
|
|
||||||
us_refL,db_refL,dur_refL = zip(*[(usL[i],dbL[i],durMsL[i]) for i in range(len(usL)) if takeIdL[i]==refTakeId])
|
us_refL,db_refL,dur_refL = zip(*[(usL[i],dbL[i],durMsL[i]) for i in range(len(usL)) if takeIdL[i]==refTakeId])
|
||||||
|
|
||||||
|
|
||||||
@ -26,8 +27,10 @@ def fit_to_reference( pkL, refTakeId ):
|
|||||||
if takeId == refTakeId:
|
if takeId == refTakeId:
|
||||||
db_outL += db0L
|
db_outL += db0L
|
||||||
else:
|
else:
|
||||||
db1V = elbow.fit_points_to_reference(us0L,db0L,us_refL,db_refL)
|
db1V = elbow.fit_points_to_reference(us0L,db0L,us_refL,db_refL)
|
||||||
db_outL += db1V.tolist()
|
|
||||||
|
if db1V is not None:
|
||||||
|
db_outL += db1V.tolist()
|
||||||
|
|
||||||
us_outL += us0L
|
us_outL += us0L
|
||||||
dur_outL+= dur0L
|
dur_outL+= dur0L
|
||||||
@ -37,18 +40,24 @@ def fit_to_reference( pkL, refTakeId ):
|
|||||||
|
|
||||||
|
|
||||||
def get_merged_pulse_db_measurements( inDir, midi_pitch, analysisArgsD ):
|
def get_merged_pulse_db_measurements( inDir, midi_pitch, analysisArgsD ):
|
||||||
|
|
||||||
inDir = os.path.join(inDir,"%i" % (midi_pitch))
|
inDir = os.path.join(inDir,"%i" % (midi_pitch))
|
||||||
|
|
||||||
takeDirL = os.listdir(inDir)
|
takeDirL = os.listdir(inDir)
|
||||||
|
|
||||||
pkL = []
|
pkL = []
|
||||||
|
|
||||||
|
refTakeId = None
|
||||||
usRefL = None
|
usRefL = None
|
||||||
dbRefL = None
|
dbRefL = None
|
||||||
|
|
||||||
# for each take in this directory
|
# for each take in this directory
|
||||||
for take_number in range(len(takeDirL)):
|
for take_folder in takeDirL:
|
||||||
|
|
||||||
|
take_number = int(take_folder)
|
||||||
|
|
||||||
|
if refTakeId is None:
|
||||||
|
refTakeId = take_number
|
||||||
|
|
||||||
# analyze this takes audio and locate the note peaks
|
# analyze this takes audio and locate the note peaks
|
||||||
r = rms_analysis.rms_analysis_main( os.path.join(inDir,str(take_number)), midi_pitch, **analysisArgsD )
|
r = rms_analysis.rms_analysis_main( os.path.join(inDir,str(take_number)), midi_pitch, **analysisArgsD )
|
||||||
@ -57,16 +66,27 @@ def get_merged_pulse_db_measurements( inDir, midi_pitch, analysisArgsD ):
|
|||||||
for db,us,stats in zip(r.pkDbL,r.pkUsL,r.statsL):
|
for db,us,stats in zip(r.pkDbL,r.pkUsL,r.statsL):
|
||||||
pkL.append( (db,us,stats.durMs,take_number) )
|
pkL.append( (db,us,stats.durMs,take_number) )
|
||||||
|
|
||||||
pkL = fit_to_reference( pkL, 0 )
|
|
||||||
|
|
||||||
# sort the peaks on increasing attack pulse microseconds
|
|
||||||
pkL = sorted( pkL, key= lambda x: x[1] )
|
|
||||||
|
|
||||||
# merge sample points that separated by less than 'minSampleDistUs' milliseconds
|
pkUsL = []
|
||||||
#pkL = merge_close_sample_points( pkL, analysisArgsD['minSampleDistUs'] )
|
pkDbL = []
|
||||||
|
durMsL = []
|
||||||
|
takeIdL = []
|
||||||
|
holdDutyPctL = []
|
||||||
|
|
||||||
# split pkL
|
if refTakeId is None:
|
||||||
pkDbL,pkUsL,durMsL,takeIdL = tuple(zip(*pkL))
|
print("No valid data files at %s pitch:%i" % (inDir,midi_pitch))
|
||||||
|
else:
|
||||||
|
|
||||||
|
pkL = fit_to_reference( pkL, refTakeId )
|
||||||
|
|
||||||
|
# sort the peaks on increasing attack pulse microseconds
|
||||||
|
pkL = sorted( pkL, key= lambda x: x[1] )
|
||||||
|
|
||||||
|
# merge sample points that separated by less than 'minSampleDistUs' milliseconds
|
||||||
|
#pkL = merge_close_sample_points( pkL, analysisArgsD['minSampleDistUs'] )
|
||||||
|
|
||||||
|
# split pkL
|
||||||
|
pkDbL,pkUsL,durMsL,takeIdL = tuple(zip(*pkL))
|
||||||
|
|
||||||
return pkUsL,pkDbL,durMsL,takeIdL,r.holdDutyPctL
|
return pkUsL,pkDbL,durMsL,takeIdL,r.holdDutyPctL
|
||||||
|
|
||||||
@ -208,7 +228,7 @@ def get_resample_points( usL, dbL, durMsL, takeIdL, minDurMs, minDb, noiseLimitP
|
|||||||
return reUsL, reDbL, noiseL, resampleL, skipL, firstAudibleIdx, firstNonSkipIdx
|
return reUsL, reDbL, noiseL, resampleL, skipL, firstAudibleIdx, firstNonSkipIdx
|
||||||
|
|
||||||
def get_resample_points_wrap( inDir, midi_pitch, analysisArgsD ):
|
def get_resample_points_wrap( inDir, midi_pitch, analysisArgsD ):
|
||||||
|
|
||||||
usL, dbL, durMsL,_,_ = get_merged_pulse_db_measurements( inDir, midi_pitch, analysisArgsD['rmsAnalysisArgs'] )
|
usL, dbL, durMsL,_,_ = get_merged_pulse_db_measurements( inDir, midi_pitch, analysisArgsD['rmsAnalysisArgs'] )
|
||||||
|
|
||||||
reUsL,_,_,_,_,_,_ = get_resample_points( usL, dbL, durMsL, analysisArgsD['resampleMinDurMs'], analysisArgsD['resampleMinDb'], analysisArgsD['resampleNoiseLimitPct'] )
|
reUsL,_,_,_,_,_,_ = get_resample_points( usL, dbL, durMsL, analysisArgsD['resampleMinDurMs'], analysisArgsD['resampleMinDb'], analysisArgsD['resampleNoiseLimitPct'] )
|
||||||
@ -220,6 +240,7 @@ def get_resample_points_wrap( inDir, midi_pitch, analysisArgsD ):
|
|||||||
def plot_us_db_curves( ax, inDir, keyMapD, midi_pitch, analysisArgsD, plotResamplePointsFl=False, plotTakesFl=True, usMax=None ):
|
def plot_us_db_curves( ax, inDir, keyMapD, midi_pitch, analysisArgsD, plotResamplePointsFl=False, plotTakesFl=True, usMax=None ):
|
||||||
|
|
||||||
usL, dbL, durMsL, takeIdL, holdDutyPctL = get_merged_pulse_db_measurements( inDir, midi_pitch, analysisArgsD['rmsAnalysisArgs'] )
|
usL, dbL, durMsL, takeIdL, holdDutyPctL = get_merged_pulse_db_measurements( inDir, midi_pitch, analysisArgsD['rmsAnalysisArgs'] )
|
||||||
|
|
||||||
reUsL, reDbL, noiseL, resampleL, skipL, firstAudibleIdx, firstNonSkipIdx = get_resample_points( usL, dbL, durMsL, takeIdL, analysisArgsD['resampleMinDurMs'], analysisArgsD['resampleMinDb'], analysisArgsD['resampleNoiseLimitPct'] )
|
reUsL, reDbL, noiseL, resampleL, skipL, firstAudibleIdx, firstNonSkipIdx = get_resample_points( usL, dbL, durMsL, takeIdL, analysisArgsD['resampleMinDurMs'], analysisArgsD['resampleMinDb'], analysisArgsD['resampleNoiseLimitPct'] )
|
||||||
|
|
||||||
# plot first audible and non-skip position
|
# plot first audible and non-skip position
|
||||||
@ -303,14 +324,15 @@ def plot_us_db_curves( ax, inDir, keyMapD, midi_pitch, analysisArgsD, plotResamp
|
|||||||
|
|
||||||
ax.set_ylabel( "%i %s %s" % (midi_pitch, keyMapD[midi_pitch]['type'],keyMapD[midi_pitch]['class']))
|
ax.set_ylabel( "%i %s %s" % (midi_pitch, keyMapD[midi_pitch]['type'],keyMapD[midi_pitch]['class']))
|
||||||
|
|
||||||
def plot_us_db_curves_main( inDir, cfg, pitchL, plotTakesFl=True, usMax=None ):
|
def plot_us_db_curves_main( inDir, cfg, pitchL, plotTakesFl=True, usMax=None, printDir="" ):
|
||||||
|
|
||||||
analysisArgsD = cfg.analysisArgs
|
analysisArgsD = cfg.analysisArgs
|
||||||
keyMapD = { d['midi']:d for d in cfg.key_mapL }
|
keyMapD = { d['midi']:d for d in cfg.key_mapL }
|
||||||
axN = len(pitchL)
|
axN = len(pitchL)
|
||||||
fig,axL = plt.subplots(axN,1,sharex=True)
|
fig,axL = plt.subplots(axN,1,sharex=True)
|
||||||
if axN == 1:
|
if axN == 1:
|
||||||
axL = [axL]
|
axL = [axL]
|
||||||
|
|
||||||
fig.set_size_inches(18.5, 10.5*axN)
|
fig.set_size_inches(18.5, 10.5*axN)
|
||||||
|
|
||||||
for ax,midi_pitch in zip(axL,pitchL):
|
for ax,midi_pitch in zip(axL,pitchL):
|
||||||
@ -318,6 +340,9 @@ def plot_us_db_curves_main( inDir, cfg, pitchL, plotTakesFl=True, usMax=None ):
|
|||||||
|
|
||||||
if plotTakesFl:
|
if plotTakesFl:
|
||||||
plt.legend()
|
plt.legend()
|
||||||
|
|
||||||
|
if printDir:
|
||||||
|
plt.savefig(os.path.join(printDir,"us_db.png"),format="png")
|
||||||
|
|
||||||
plt.show()
|
plt.show()
|
||||||
|
|
||||||
@ -332,16 +357,16 @@ def plot_all_noise_curves( inDir, cfg, pitchL=None ):
|
|||||||
|
|
||||||
for midi_pitch in pitchL:
|
for midi_pitch in pitchL:
|
||||||
|
|
||||||
print(midi_pitch)
|
|
||||||
|
|
||||||
usL, dbL, durMsL, takeIdL, holdDutyPctL = get_merged_pulse_db_measurements( inDir, midi_pitch, cfg.analysisArgs['rmsAnalysisArgs'] )
|
usL, dbL, durMsL, takeIdL, holdDutyPctL = get_merged_pulse_db_measurements( inDir, midi_pitch, cfg.analysisArgs['rmsAnalysisArgs'] )
|
||||||
|
|
||||||
scoreV = np.abs( rms_analysis.samples_to_linear_residual( usL, dbL) * 100.0 / dbL )
|
scoreV = np.abs( rms_analysis.samples_to_linear_residual( usL, dbL) * 100.0 / dbL )
|
||||||
|
|
||||||
minDurMs = cfg.analysisArgs['resampleMinDurMs']
|
minDurMs = cfg.analysisArgs['resampleMinDurMs']
|
||||||
minDb = cfg.analysisArgs['resampleMinDb'],
|
minDb = cfg.analysisArgs['resampleMinDb']
|
||||||
noiseLimitPct = cfg.analysisArgs['resampleNoiseLimitPct']
|
noiseLimitPct = cfg.analysisArgs['resampleNoiseLimitPct']
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
skipIdxL, firstAudibleIdx, firstNonSkipIdx = get_dur_skip_indexes( durMsL, dbL, scoreV.tolist(), takeIdL, minDurMs, minDb, noiseLimitPct )
|
skipIdxL, firstAudibleIdx, firstNonSkipIdx = get_dur_skip_indexes( durMsL, dbL, scoreV.tolist(), takeIdL, minDurMs, minDb, noiseLimitPct )
|
||||||
|
|
||||||
|
|
||||||
@ -364,18 +389,22 @@ def plot_all_noise_curves( inDir, cfg, pitchL=None ):
|
|||||||
plt.legend()
|
plt.legend()
|
||||||
plt.show()
|
plt.show()
|
||||||
|
|
||||||
def plot_min_max_2_db( inDir, cfg, pitchL=None, takeId=2 ):
|
def plot_min_max_2_db( inDir, cfg, pitchL=None, takeId=2, printDir=None ):
|
||||||
|
|
||||||
pitchFolderL = os.listdir(inDir)
|
pitchFolderL = os.listdir(inDir)
|
||||||
|
|
||||||
|
print(pitchL)
|
||||||
|
|
||||||
if pitchL is None:
|
if pitchL is None:
|
||||||
pitchL = [ int( int(pitchFolder) ) for pitchFolder in pitchFolderL ]
|
pitchL = [ int( int(pitchFolder) ) for pitchFolder in pitchFolderL ]
|
||||||
|
|
||||||
|
print(pitchL)
|
||||||
okL = []
|
|
||||||
|
okL = []
|
||||||
outPitchL = []
|
outPitchL = []
|
||||||
minDbL = []
|
minDbL = []
|
||||||
maxDbL = []
|
maxDbL = []
|
||||||
|
|
||||||
for midi_pitch in pitchL:
|
for midi_pitch in pitchL:
|
||||||
|
|
||||||
print(midi_pitch)
|
print(midi_pitch)
|
||||||
@ -384,11 +413,8 @@ def plot_min_max_2_db( inDir, cfg, pitchL=None, takeId=2 ):
|
|||||||
|
|
||||||
okL.append(False)
|
okL.append(False)
|
||||||
|
|
||||||
takeId = len(set(takeIdL))-1
|
|
||||||
|
|
||||||
db_maxL = sorted(dbL)
|
db_maxL = sorted(dbL)
|
||||||
maxDbL.append( np.mean(db_maxL[-5:]) )
|
maxDbL.append( np.mean(db_maxL[-5:]) )
|
||||||
|
|
||||||
|
|
||||||
usL,dbL = zip(*[(usL[i],dbL[i]) for i in range(len(usL)) if takeIdL[i]==takeId ])
|
usL,dbL = zip(*[(usL[i],dbL[i]) for i in range(len(usL)) if takeIdL[i]==takeId ])
|
||||||
|
|
||||||
@ -414,10 +440,14 @@ def plot_min_max_2_db( inDir, cfg, pitchL=None, takeId=2 ):
|
|||||||
ax.text( pitch, min_db, "%i %s %s" % (pitch, keyMapD[pitch]['type'],keyMapD[pitch]['class']), color=c)
|
ax.text( pitch, min_db, "%i %s %s" % (pitch, keyMapD[pitch]['type'],keyMapD[pitch]['class']), color=c)
|
||||||
ax.text( pitch, max_db, "%i %s %s" % (pitch, keyMapD[pitch]['type'],keyMapD[pitch]['class']), color=c)
|
ax.text( pitch, max_db, "%i %s %s" % (pitch, keyMapD[pitch]['type'],keyMapD[pitch]['class']), color=c)
|
||||||
|
|
||||||
|
|
||||||
|
if printDir:
|
||||||
|
plt.savefig(os.path.join(printDir,"min_max_db_2.png"),format="png")
|
||||||
|
|
||||||
|
|
||||||
plt.show()
|
plt.show()
|
||||||
|
|
||||||
def plot_min_db_manual( inDir, cfg ):
|
def plot_min_db_manual( inDir, cfg, printDir=None ):
|
||||||
|
|
||||||
pitchL = list(cfg.manualMinD.keys())
|
pitchL = list(cfg.manualMinD.keys())
|
||||||
|
|
||||||
@ -463,8 +493,10 @@ def plot_min_db_manual( inDir, cfg ):
|
|||||||
|
|
||||||
if midi_pitch in cfg.manualAnchorPitchMaxDbL:
|
if midi_pitch in cfg.manualAnchorPitchMaxDbL:
|
||||||
anchorMaxDbL.append( max_db )
|
anchorMaxDbL.append( max_db )
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
# Form the complete set of min/max db levels for each pitch by interpolating the
|
# Form the complete set of min/max db levels for each pitch by interpolating the
|
||||||
# db values between the manually selected anchor points.
|
# db values between the manually selected anchor points.
|
||||||
@ -491,15 +523,14 @@ def plot_min_db_manual( inDir, cfg ):
|
|||||||
with open("minInterpDb.json",'w') as f:
|
with open("minInterpDb.json",'w') as f:
|
||||||
json.dump( { "pitchL":pitchL, "minDbL":list(interpMinDbL), "maxDbL":list(interpMaxDbL) }, f )
|
json.dump( { "pitchL":pitchL, "minDbL":list(interpMinDbL), "maxDbL":list(interpMaxDbL) }, f )
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
if printDir:
|
||||||
|
plt.savefig(os.path.join(printDir,"manual_db.png"),format="png")
|
||||||
|
|
||||||
plt.show()
|
plt.show()
|
||||||
|
|
||||||
def plot_min_max_db( inDir, cfg, pitchL=None ):
|
def plot_min_max_db( inDir, cfg, pitchL=None, printDir=None ):
|
||||||
|
|
||||||
pitchFolderL = os.listdir(inDir)
|
pitchFolderL = os.listdir(inDir)
|
||||||
|
|
||||||
@ -518,7 +549,7 @@ def plot_min_max_db( inDir, cfg, pitchL=None ):
|
|||||||
scoreV = np.abs( rms_analysis.samples_to_linear_residual( usL, dbL) * 100.0 / dbL )
|
scoreV = np.abs( rms_analysis.samples_to_linear_residual( usL, dbL) * 100.0 / dbL )
|
||||||
|
|
||||||
minDurMs = cfg.analysisArgs['resampleMinDurMs']
|
minDurMs = cfg.analysisArgs['resampleMinDurMs']
|
||||||
minDb = cfg.analysisArgs['resampleMinDb'],
|
minDb = cfg.analysisArgs['resampleMinDb']
|
||||||
noiseLimitPct = cfg.analysisArgs['resampleNoiseLimitPct']
|
noiseLimitPct = cfg.analysisArgs['resampleNoiseLimitPct']
|
||||||
|
|
||||||
skipIdxL, firstAudibleIdx, firstNonSkipIdx = get_dur_skip_indexes( durMsL, dbL, takeIdL, scoreV.tolist(), minDurMs, minDb, noiseLimitPct )
|
skipIdxL, firstAudibleIdx, firstNonSkipIdx = get_dur_skip_indexes( durMsL, dbL, takeIdL, scoreV.tolist(), minDurMs, minDb, noiseLimitPct )
|
||||||
@ -548,6 +579,9 @@ def plot_min_max_db( inDir, cfg, pitchL=None ):
|
|||||||
|
|
||||||
ax.text( pitch, db, "%i %s %s" % (pitch, keyMapD[pitch]['type'],keyMapD[pitch]['class']))
|
ax.text( pitch, db, "%i %s %s" % (pitch, keyMapD[pitch]['type'],keyMapD[pitch]['class']))
|
||||||
|
|
||||||
|
if printDir:
|
||||||
|
plt.savefig(os.path.join(printDir,"min_max_db.png"),format="png")
|
||||||
|
|
||||||
|
|
||||||
plt.show()
|
plt.show()
|
||||||
|
|
||||||
@ -575,7 +609,7 @@ def estimate_us_to_db_map( inDir, cfg, minMapDb=16.0, maxMapDb=26.0, incrMapDb=0
|
|||||||
scoreV = np.abs( rms_analysis.samples_to_linear_residual( usL, dbL) * 100.0 / dbL )
|
scoreV = np.abs( rms_analysis.samples_to_linear_residual( usL, dbL) * 100.0 / dbL )
|
||||||
|
|
||||||
minDurMs = cfg.analysisArgs['resampleMinDurMs']
|
minDurMs = cfg.analysisArgs['resampleMinDurMs']
|
||||||
minDb = cfg.analysisArgs['resampleMinDb'],
|
minDb = cfg.analysisArgs['resampleMinDb']
|
||||||
noiseLimitPct = cfg.analysisArgs['resampleNoiseLimitPct']
|
noiseLimitPct = cfg.analysisArgs['resampleNoiseLimitPct']
|
||||||
|
|
||||||
# get the set of samples that are not valid (too short, too quiet, too noisy)
|
# get the set of samples that are not valid (too short, too quiet, too noisy)
|
||||||
@ -633,7 +667,7 @@ def estimate_us_to_db_map( inDir, cfg, minMapDb=16.0, maxMapDb=26.0, incrMapDb=0
|
|||||||
|
|
||||||
return mapD, list(dbS)
|
return mapD, list(dbS)
|
||||||
|
|
||||||
def plot_us_to_db_map( inDir, cfg, minMapDb=16.0, maxMapDb=26.0, incrMapDb=1.0, pitchL=None ):
|
def plot_us_to_db_map( inDir, cfg, minMapDb=16.0, maxMapDb=26.0, incrMapDb=1.0, pitchL=None, printDir=None ):
|
||||||
|
|
||||||
fig,ax = plt.subplots()
|
fig,ax = plt.subplots()
|
||||||
|
|
||||||
@ -644,22 +678,28 @@ def plot_us_to_db_map( inDir, cfg, minMapDb=16.0, maxMapDb=26.0, incrMapDb=1.0,
|
|||||||
|
|
||||||
u_dL = [ (d['us_avg'],d['us_cls'],d['db_avg'],d['us_std'],d['us_min'],d['us_max'],d['db_std']) for loDb, d in dbD.items() if d['us_avg'] != 0 ]
|
u_dL = [ (d['us_avg'],d['us_cls'],d['db_avg'],d['us_std'],d['us_min'],d['us_max'],d['db_std']) for loDb, d in dbD.items() if d['us_avg'] != 0 ]
|
||||||
|
|
||||||
# get the us/db lists for this pitch
|
if u_dL:
|
||||||
usL,uscL,dbL,ussL,usnL,usxL,dbsL = zip(*u_dL)
|
|
||||||
|
# get the us/db lists for this pitch
|
||||||
|
usL,uscL,dbL,ussL,usnL,usxL,dbsL = zip(*u_dL)
|
||||||
|
|
||||||
# plot central curve and std dev's
|
# plot central curve and std dev's
|
||||||
p = ax.plot(usL,dbL, marker='.', label=str(pitch))
|
p = ax.plot(usL,dbL, marker='.', label=str(pitch))
|
||||||
ax.plot(uscL,dbL, marker='x', label=str(pitch), color=p[0].get_color(), linestyle='None')
|
ax.plot(uscL,dbL, marker='x', label=str(pitch), color=p[0].get_color(), linestyle='None')
|
||||||
ax.plot(usL,np.array(dbL)+dbsL, color=p[0].get_color(), alpha=0.3)
|
ax.plot(usL,np.array(dbL)+dbsL, color=p[0].get_color(), alpha=0.3)
|
||||||
ax.plot(usL,np.array(dbL)-dbsL, color=p[0].get_color(), alpha=0.3)
|
ax.plot(usL,np.array(dbL)-dbsL, color=p[0].get_color(), alpha=0.3)
|
||||||
|
|
||||||
# plot us error bars
|
# plot us error bars
|
||||||
for db,us,uss,us_min,us_max in zip(dbL,usL,ussL,usnL,usxL):
|
for db,us,uss,us_min,us_max in zip(dbL,usL,ussL,usnL,usxL):
|
||||||
ax.plot([us_min,us_max],[db,db], color=p[0].get_color(), alpha=0.3 )
|
ax.plot([us_min,us_max],[db,db], color=p[0].get_color(), alpha=0.3 )
|
||||||
ax.plot([us-uss,us+uss],[db,db], color=p[0].get_color(), alpha=0.3, marker='.', linestyle='None' )
|
ax.plot([us-uss,us+uss],[db,db], color=p[0].get_color(), alpha=0.3, marker='.', linestyle='None' )
|
||||||
|
|
||||||
|
|
||||||
plt.legend()
|
plt.legend()
|
||||||
|
|
||||||
|
if printDir:
|
||||||
|
plt.savefig(os.path.join(printDir,"us_db_map.png"),format="png")
|
||||||
|
|
||||||
plt.show()
|
plt.show()
|
||||||
|
|
||||||
def report_take_ids( inDir ):
|
def report_take_ids( inDir ):
|
||||||
@ -760,9 +800,11 @@ def gen_vel_map( inDir, cfg, minMaxDbFn, dynLevelN, cacheFn ):
|
|||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
|
|
||||||
inDir = sys.argv[1]
|
printDir =os.path.expanduser( "~/src/picadae_ac_3/doc")
|
||||||
cfgFn = sys.argv[2]
|
cfgFn = sys.argv[1]
|
||||||
mode = sys.argv[3]
|
inDir = sys.argv[2]
|
||||||
|
mode = sys.argv[3]
|
||||||
|
|
||||||
if len(sys.argv) <= 4:
|
if len(sys.argv) <= 4:
|
||||||
pitchL = None
|
pitchL = None
|
||||||
else:
|
else:
|
||||||
@ -771,21 +813,23 @@ if __name__ == "__main__":
|
|||||||
cfg = parse_yaml_cfg( cfgFn )
|
cfg = parse_yaml_cfg( cfgFn )
|
||||||
|
|
||||||
if mode == 'us_db':
|
if mode == 'us_db':
|
||||||
plot_us_db_curves_main( inDir, cfg, pitchL, plotTakesFl=True,usMax=None )
|
plot_us_db_curves_main( inDir, cfg, pitchL, plotTakesFl=True,usMax=None, printDir=printDir )
|
||||||
elif mode == 'noise':
|
elif mode == 'noise':
|
||||||
plot_all_noise_curves( inDir, cfg, pitchL )
|
plot_all_noise_curves( inDir, cfg, pitchL )
|
||||||
elif mode == 'min_max':
|
elif mode == 'min_max':
|
||||||
plot_min_max_db( inDir, cfg, pitchL )
|
plot_min_max_db( inDir, cfg, pitchL, printDir=printDir )
|
||||||
elif mode == 'min_max_2':
|
elif mode == 'min_max_2':
|
||||||
plot_min_max_2_db( inDir, cfg, pitchL )
|
takeId = pitchL[-1]
|
||||||
|
del pitchL[-1]
|
||||||
|
plot_min_max_2_db( inDir, cfg, pitchL, takeId=takeId, printDir=printDir )
|
||||||
elif mode == 'us_db_map':
|
elif mode == 'us_db_map':
|
||||||
plot_us_to_db_map( inDir, cfg, pitchL=pitchL )
|
plot_us_to_db_map( inDir, cfg, pitchL=pitchL, printDir=printDir )
|
||||||
elif mode == 'audacity':
|
elif mode == 'audacity':
|
||||||
rms_analysis.write_audacity_label_files( inDir, cfg.analysisArgs['rmsAnalysisArgs'] )
|
rms_analysis.write_audacity_label_files( inDir, cfg.analysisArgs['rmsAnalysisArgs'] )
|
||||||
elif mode == 'rpt_take_ids':
|
elif mode == 'rpt_take_ids':
|
||||||
report_take_ids( inDir )
|
report_take_ids( inDir )
|
||||||
elif mode == 'manual_db':
|
elif mode == 'manual_db':
|
||||||
plot_min_db_manual( inDir, cfg )
|
plot_min_db_manual( inDir, cfg, printDir=printDir )
|
||||||
elif mode == 'gen_vel_map':
|
elif mode == 'gen_vel_map':
|
||||||
gen_vel_map( inDir, cfg, "minInterpDb.json", 9, "cache_us_db.json" )
|
gen_vel_map( inDir, cfg, "minInterpDb.json", 9, "cache_us_db.json" )
|
||||||
elif mode == 'cache_us_db':
|
elif mode == 'cache_us_db':
|
||||||
|
@ -201,12 +201,14 @@ def select_first_stable_note_by_delta_db( pkDbL, pkUsL=None, maxPulseUs=0.1 ):
|
|||||||
return None
|
return None
|
||||||
|
|
||||||
def note_stats( r, decay_pct=50.0, extraDurSearchMs=500 ):
|
def note_stats( r, decay_pct=50.0, extraDurSearchMs=500 ):
|
||||||
|
""" Collect some statistics and markers for each note in the sequence. """
|
||||||
statsL = []
|
statsL = []
|
||||||
|
|
||||||
srate = r.rms_srate
|
srate = r.rms_srate
|
||||||
|
|
||||||
qmax = 0
|
qmax = 0
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
for i,(begSmpMs, endSmpMs) in enumerate(r.eventTimeL):
|
for i,(begSmpMs, endSmpMs) in enumerate(r.eventTimeL):
|
||||||
|
|
||||||
@ -214,6 +216,7 @@ def note_stats( r, decay_pct=50.0, extraDurSearchMs=500 ):
|
|||||||
endSmpIdx = int(round(srate * (endSmpMs + extraDurSearchMs) / 1000.0))
|
endSmpIdx = int(round(srate * (endSmpMs + extraDurSearchMs) / 1000.0))
|
||||||
pkSmpIdx = r.pkIdxL[i]
|
pkSmpIdx = r.pkIdxL[i]
|
||||||
|
|
||||||
|
|
||||||
durMs = measure_duration_ms( r.rmsDbV, srate, pkSmpIdx, endSmpIdx, decay_pct )
|
durMs = measure_duration_ms( r.rmsDbV, srate, pkSmpIdx, endSmpIdx, decay_pct )
|
||||||
|
|
||||||
bi = pkSmpIdx
|
bi = pkSmpIdx
|
||||||
@ -232,7 +235,15 @@ def note_stats( r, decay_pct=50.0, extraDurSearchMs=500 ):
|
|||||||
hmRmsDb_u = 0.0 if ei >= len(r.rmsDbV) else np.mean(r.rmsDbV[bi:ei])
|
hmRmsDb_u = 0.0 if ei >= len(r.rmsDbV) else np.mean(r.rmsDbV[bi:ei])
|
||||||
durAvgDb = (hmRmsDb_u + tdRmsDb_u)/2.0
|
durAvgDb = (hmRmsDb_u + tdRmsDb_u)/2.0
|
||||||
|
|
||||||
statsL.append( types.SimpleNamespace(**{'begSmpSec':begSmpIdx/srate,'endSmpSec':endSmpIdx/srate,'pkSmpSec':pkSmpIdx/srate,'durMs':durMs, 'pkDb':r.pkDbL[i], 'pulse_us':r.pkUsL[i], 'quality':qualityCoeff, 'durAvgDb':durAvgDb }))
|
statsL.append( types.SimpleNamespace(**{
|
||||||
|
'begSmpSec':begSmpIdx/srate,
|
||||||
|
'endSmpSec':endSmpIdx/srate,
|
||||||
|
'pkSmpSec':pkSmpIdx/srate,
|
||||||
|
'durMs':durMs,
|
||||||
|
'pkDb':r.pkDbL[i],
|
||||||
|
'pulse_us':r.pkUsL[i],
|
||||||
|
'quality':qualityCoeff,
|
||||||
|
'durAvgDb':durAvgDb }))
|
||||||
|
|
||||||
for i,r in enumerate(statsL):
|
for i,r in enumerate(statsL):
|
||||||
statsL[i].quality = 0 if qmax <= 0 else statsL[i].quality / qmax
|
statsL[i].quality = 0 if qmax <= 0 else statsL[i].quality / qmax
|
||||||
@ -243,15 +254,19 @@ def note_stats( r, decay_pct=50.0, extraDurSearchMs=500 ):
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
def locate_peak_indexes( xV, xV_srate, eventMsL ):
|
def locate_peak_indexes( xV, xV_srate, eventMsL, audioFn ):
|
||||||
|
|
||||||
pkIdxL = []
|
pkIdxL = []
|
||||||
for begMs, endMs in eventMsL:
|
for i, (begMs, endMs) in enumerate(eventMsL):
|
||||||
|
|
||||||
begSmpIdx = int(begMs * xV_srate / 1000.0)
|
begSmpIdx = int(begMs * xV_srate / 1000.0)
|
||||||
endSmpIdx = int(endMs * xV_srate / 1000.0)
|
endSmpIdx = int(endMs * xV_srate / 1000.0)
|
||||||
|
|
||||||
pkIdxL.append( begSmpIdx + np.argmax( xV[begSmpIdx:endSmpIdx] ) )
|
if endSmpIdx != begSmpIdx:
|
||||||
|
pkIdxL.append( begSmpIdx + np.argmax( xV[begSmpIdx:endSmpIdx] ) )
|
||||||
|
else:
|
||||||
|
print("Missing peak %i : begween beg:%i ms end:%i ms : %s" % (i, begMs, endMs, audioFn ))
|
||||||
|
pkIdxL.append( None )
|
||||||
|
|
||||||
return pkIdxL
|
return pkIdxL
|
||||||
|
|
||||||
@ -304,6 +319,11 @@ def rms_analyze_one_rt_note_wrap( audioDev, annBegMs, annEndMs, midi_pitch, note
|
|||||||
|
|
||||||
sigV = buf_result.value
|
sigV = buf_result.value
|
||||||
|
|
||||||
|
if len(sigV.shape) > 1:
|
||||||
|
sigV = sigV[:,0].squeeze()
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
# get the annotated begin and end of the note as sample indexes into sigV
|
# get the annotated begin and end of the note as sample indexes into sigV
|
||||||
bi = int(round(annBegMs * audioDev.srate / 1000))
|
bi = int(round(annBegMs * audioDev.srate / 1000))
|
||||||
ei = int(round(annEndMs * audioDev.srate / 1000))
|
ei = int(round(annEndMs * audioDev.srate / 1000))
|
||||||
@ -399,47 +419,72 @@ def calibrate_recording_analysis( inDir ):
|
|||||||
|
|
||||||
def rms_analysis_main( inDir, midi_pitch, rmsWndMs=300, rmsHopMs=30, dbLinRef=0.001, harmCandN=5, harmN=3, durDecayPct=40 ):
|
def rms_analysis_main( inDir, midi_pitch, rmsWndMs=300, rmsHopMs=30, dbLinRef=0.001, harmCandN=5, harmN=3, durDecayPct=40 ):
|
||||||
|
|
||||||
|
# form the audio and meta data file names
|
||||||
seqFn = os.path.join( inDir, "seq.json")
|
seqFn = os.path.join( inDir, "seq.json")
|
||||||
audioFn = os.path.join( inDir, "audio.wav")
|
audioFn = os.path.join( inDir, "audio.wav")
|
||||||
|
|
||||||
|
# read the meta data file
|
||||||
with open( seqFn, "rb") as f:
|
with open( seqFn, "rb") as f:
|
||||||
r = json.load(f)
|
r = json.load(f)
|
||||||
|
|
||||||
|
# rad the auido file
|
||||||
srate, signalM = wavfile.read(audioFn)
|
srate, signalM = wavfile.read(audioFn)
|
||||||
|
|
||||||
|
# convert the audio signal vector to contain only the first (left) channel
|
||||||
|
if len(signalM.shape)>1:
|
||||||
|
signalM = signalM[:,0].squeeze()
|
||||||
|
|
||||||
|
# convert the auido file to floats in range [-1.0 to 1.0]
|
||||||
sigV = signalM / float(0x7fff)
|
sigV = signalM / float(0x7fff)
|
||||||
|
|
||||||
|
# calc. the RMS signal
|
||||||
tdRmsDbV, rms0_srate = audio_rms( srate, sigV, rmsWndMs, rmsHopMs, dbLinRef )
|
tdRmsDbV, rms0_srate = audio_rms( srate, sigV, rmsWndMs, rmsHopMs, dbLinRef )
|
||||||
|
|
||||||
tdPkIdxL = locate_peak_indexes( tdRmsDbV, rms0_srate, r['eventTimeL'])
|
# locate the peaks in the RMS signal
|
||||||
|
tdPkIdxL = locate_peak_indexes( tdRmsDbV, rms0_srate, r['eventTimeL'], audioFn )
|
||||||
|
|
||||||
|
# sum the first harmN harmonics to form a envelope of the audio signal (this is an alternative to the RMS signal)
|
||||||
rmsDbV, rms_srate, binHz = audio_harm_rms( srate, sigV, rmsWndMs, rmsHopMs, dbLinRef, midi_pitch, harmCandN, harmN )
|
rmsDbV, rms_srate, binHz = audio_harm_rms( srate, sigV, rmsWndMs, rmsHopMs, dbLinRef, midi_pitch, harmCandN, harmN )
|
||||||
|
|
||||||
pkIdxL = locate_peak_indexes( rmsDbV, rms_srate, r['eventTimeL'] )
|
# locate the peaks in the harmonic sum signal
|
||||||
|
pkIdxL = locate_peak_indexes( rmsDbV, rms_srate, r['eventTimeL'], audioFn )
|
||||||
|
|
||||||
|
# form the 'holdDutyPctlL' hold duty cycle transition point list
|
||||||
holdDutyPctL = None
|
holdDutyPctL = None
|
||||||
if 'holdDutyPct' in r:
|
if 'holdDutyPct' in r:
|
||||||
holdDutyPctL = [ (0, r['holdDutyPct']) ]
|
holdDutyPctL = [ (0, r['holdDutyPct']) ]
|
||||||
else:
|
else:
|
||||||
holdDutyPctL = r['holdDutyPctL']
|
holdDutyPctL = r['holdDutyPctL']
|
||||||
|
|
||||||
|
eventN = len(r['eventTimeL'])
|
||||||
|
assert( len(tdPkIdxL) == eventN and len(pkIdxL) == eventN )
|
||||||
|
|
||||||
|
# filter out all missing events that have no peak
|
||||||
|
flL = [ (tdPkIdxL[i] is not None) and (pkIdxL[i] is not None) for i in range(eventN) ]
|
||||||
|
eventTimeL = [ r['eventTimeL'][i] for i in range(eventN) if flL[i] ]
|
||||||
|
tdPkDbL = [ tdRmsDbV[tdPkIdxL[i]] for i in range(eventN) if flL[i] ]
|
||||||
|
pkDbL = [ rmsDbV[ pkIdxL[i]] for i in range(eventN) if flL[i] ]
|
||||||
|
#pkUsL = [ r['pulseUsL'][pkIdxL[i]] for i in range(eventN) if flL[i] ]
|
||||||
|
tdPkIdxL = [ i for i in tdPkIdxL if i is not None ]
|
||||||
|
pkIdxL = [ i for i in pkIdxL if i is not None ]
|
||||||
|
|
||||||
|
# form the result record
|
||||||
r = types.SimpleNamespace(**{
|
r = types.SimpleNamespace(**{
|
||||||
"audio_srate":srate,
|
"audio_srate":srate,
|
||||||
"eventTimeMsL":r['eventTimeL'],
|
"eventTimeMsL": eventTimeL, #r['eventTimeL'],
|
||||||
"tdRmsDbV": tdRmsDbV,
|
"tdRmsDbV": tdRmsDbV,
|
||||||
"tdPkIdxL": tdPkIdxL,
|
"tdPkIdxL": tdPkIdxL,
|
||||||
"tdPkDbL": [ tdRmsDbV[i] for i in tdPkIdxL ],
|
"tdPkDbL": tdPkDbL, # [ tdRmsDbV[i] for i in tdPkIdxL ],
|
||||||
"binHz": binHz,
|
"binHz": binHz,
|
||||||
"rmsDbV":rmsDbV,
|
"rmsDbV": rmsDbV,
|
||||||
"rms_srate":rms_srate,
|
"rms_srate":rms_srate,
|
||||||
"pkIdxL":pkIdxL, # pkIdxL[ len(pulsUsL) ] - indexes into rmsDbV[] of peaks
|
"pkIdxL":pkIdxL, # pkIdxL[ len(pulsUsL) ] - indexes into rmsDbV[] of peaks
|
||||||
"eventTimeL":r['eventTimeL'],
|
"eventTimeL": eventTimeL, #r['eventTimeL'],
|
||||||
"holdDutyPctL":holdDutyPctL,
|
"holdDutyPctL":holdDutyPctL,
|
||||||
'pkDbL': [ rmsDbV[ i ] for i in pkIdxL ],
|
'pkDbL': pkDbL, # [ rmsDbV[ i ] for i in pkIdxL ],
|
||||||
'pkUsL':r['pulseUsL'] })
|
'pkUsL': r['pulseUsL'] }) # r['pulseUsL']
|
||||||
|
|
||||||
|
#
|
||||||
statsL = note_stats(r,durDecayPct)
|
statsL = note_stats(r,durDecayPct)
|
||||||
|
|
||||||
setattr(r,"statsL", statsL )
|
setattr(r,"statsL", statsL )
|
||||||
|