def music_extractor(audio_file, sig_file, profile=None, store_frames=False, format='yaml'): if profile: extractor = MusicExtractor(profile=profile) else: extractor = MusicExtractor() poolStats, poolFrames = extractor(audio_file) folder = os.path.dirname(sig_file) if not os.path.exists(folder): os.makedirs(folder) elif os.path.isfile(folder): raise EssentiaError( 'Cannot create directory {} .There exist a file with the same name. Aborting analysis.' .format(folder)) output = YamlOutput(filename=sig_file + '.sig', format=format) output(poolStats) if store_frames: YamlOutput(filename=sig_file + '.frames.sig', format=format)(poolFrames)
def doSegmentation(inputFilename, audio, pool, options): # options segtype = options[namespace]['type'] minimumLength = options[namespace]['minimumSegmentsLength'] writeFile = options[namespace]['writeSegmentsAudioFile'] sampleRate = options['sampleRate'] if segtype == 'fromFile': segments = [ map(float, l.strip().split('\t')) for l in open(options[namespace]['segmentsFile'], 'r').readlines() ] else: if segtype == 'maxEnergy': onsets = segmentation_max_energy.compute(pool, options) elif segtype == 'bic': # Bayesian Information Criterion (Bic) segmentation onsets = segmentation_bic.compute(pool, options) else: raise EssentiaError('Unknown segmentation type: ' + segtype) # creating segment wave file if writeFile: outputFilename = inputFilename + '.segments.wav' print 'Creating segments audio file ' + outputFilename + '...' audioOnsetsMarker = essentia.AudioOnsetsMarker( filename=outputFilename, sampleRate=sampleRate) audioOnsetsMarker(audio, onsets) # transforming the onsets into segments = pairs of onsets segments = [] for onset, onsetNext in zip(onsets[:-1], onsets[1:]): segments.append([onset, onsetNext]) if options['verbose']: if len(segments) > 0: print 'Segments : ', for segment in segments: print '[', print_onset(segment[0]) print ",", print_onset(segment[1]) print '] ', else: print 'No segments found!' print return segments
def add(self, name, value, scope = CurrentScope): if isinstance(value, tuple): raise EssentiaError('error when adding %s: you can\'t add tuples to the pool' % name) if scope == self.GlobalScope: self.statsBlackList.append((self.__currentNamespace, name)) scope = self.__globalScope elif scope == self.CurrentScope: scope = self.__currentScope try: self.descriptors[self.__currentNamespace][name]['values'].append(value) self.descriptors[self.__currentNamespace][name]['scopes'].append(scope) except KeyError: self.descriptors[self.__currentNamespace][name] = {'values' : [value], 'scopes' : [scope]}
def batch_music_extractor(audio_dir, output_dir, generate_log=True, audio_types=None, profile=None, store_frames=False, skip_analyzed=False, format='yaml', jobs=0): """ Processes every audio file matching `audio_types` in `audio_dir` with MusicExtractor. The generated .sig yaml/json files are stored in `output_dir` matching the folder structure found in `audio_dir`. """ if not audio_types: audio_types = ('.WAV', '.AIFF', '.FLAC', '.MP3', '.OGG') print("Audio files extensions considered by default: " + ' '.join(audio_types)) else: audio_types = tuple(audio_types) print("Searching for audio files extensions: " + ' '.join(audio_types)) print("") output_dir = os.path.abspath(output_dir) if profile: assert os.path.isfile(profile) if jobs == 0: try: jobs = cpu_count() except NotImplementedError: print("Failed to automatically detect the cpu count, the analysis will try to continue with 4 jobs. For a different behavior change the `job` parameter.") jobs = 4 # find all audio files and prepare folder structure in the output folder os.chdir(audio_dir) skipped_count = 0 skipped_files = [] cmd_lines = [] for root, dirnames, filenames in os.walk("."): for filename in filenames: if filename.upper().endswith(audio_types): audio_file = os.path.relpath(os.path.join(root, filename)) audio_file_abs = os.path.join(audio_dir, audio_file) sig_file = os.path.join(output_dir, audio_file) if skip_analyzed: if os.path.isfile(sig_file + '.sig'): print("Found descriptor file for " + audio_file + ", skipping...") skipped_files.append(audio_file) skipped_count += 1 continue folder = os.path.dirname(sig_file) if not os.path.exists(folder): os.makedirs(folder) elif os.path.isfile(folder): raise EssentiaError('Cannot create directory {} .There exist a file with the same name. Aborting analysis.'.format(folder)) cmd_line = [ sys.executable, os.path.join(os.path.dirname(__file__), 'extractors/music_extractor.py'), audio_file_abs, sig_file, '--format', format ] if store_frames: cmd_line += ['--store_frames'] if profile: cmd_line += ['--profile', profile] cmd_lines.append(cmd_line) # analyze errors, oks = 0, 0 if len(cmd_lines) > 0: p = Pool(jobs) outs = p.map(__subprocess__, cmd_lines) status, cmd, stderr = zip(*outs) oks, errors = 0, 0 for i in status: if i == 0: oks += 1 else: errors += 1 summary = "Analysis done. {} files have been skipped due to errors, {} were processed and {} already existed.".format( errors, oks, skipped_count) print(summary) # generate log if generate_log: log = [summary] if errors > 0: log += ['Errors:'] + ['"{}"\n{}\n\n'.format(cmd[idx], stderr[idx]) for idx, i in enumerate(status) if i != 0] if oks > 0: log += ['Oks:'] + [cmd[idx] for idx, i in enumerate(status) if i == 0] if skipped_count > 0: log += ['Skipped files:'] + skipped_files if not os.path.exists(output_dir): os.makedirs(output_dir) with open(os.path.join(output_dir, 'log'), 'w') as f: f.write('\n'.join(log))
def _batch_extractor(audio_dir, output_dir, extractor_cmd, output_extension, generate_log=True, audio_types=None, skip_analyzed=False, jobs=0, verbose=True): if not audio_types: audio_types = ('.wav', '.aiff', '.flac', '.mp3', '.ogg') print("Audio files extensions considered by default: " + ', '.join(audio_types)) else: if type(audio_types) == str: audio_types = [audio_types] audio_types = tuple(audio_types) audio_types = tuple([i.lower() for i in audio_types]) print("Searching for audio files extensions: " + ', '.join(audio_types)) print("") output_dir = os.path.abspath(output_dir) audio_dir = os.path.abspath(audio_dir) if jobs == 0: try: jobs = cpu_count() except NotImplementedError: print('Failed to automatically detect the cpu count, ' 'the analysis will try to continue with 4 jobs. ' 'For a different behavior change the `job` parameter.') jobs = 4 skipped_count = 0 skipped_files = [] cmd_lines = [] for root, _, filenames in os.walk(audio_dir): for filename in filenames: if filename.lower().endswith(audio_types): audio_file = os.path.join(audio_dir, root, filename) out_file = os.path.join(output_dir, output_dir, filename) if skip_analyzed: if os.path.isfile('{}.{}'.format(out_file, output_extension)): print("Found descriptor file for " + audio_file + ", skipping...") skipped_files.append(audio_file) skipped_count += 1 continue folder = os.path.dirname(out_file) if os.path.isfile(folder): raise EssentiaError('Cannot create directory "{}". ' 'There is a file with the same name. ' 'Aborting analysis.'.format(folder)) else: os.makedirs(folder, exist_ok=True) cmd_lines.append(extractor_cmd + [audio_file, out_file]) # analyze log_lines = [] total, errors, oks = 0, 0, 0 if cmd_lines: p = Pool(jobs) outs = p.map(partial(_subprocess, verbose=verbose), cmd_lines) total = len(outs) status, cmd, stderr = zip(*outs) oks, errors = 0, 0 for i, cmd_idx, err in zip(status, cmd, stderr): if i == 0: oks += 1 log_lines.append('"{}" ok!'.format(cmd_idx)) else: errors += 1 log_lines.append('"{}" failed'.format(cmd_idx)) log_lines.append(' "{}"'.format(err)) summary = ( "Analysis done for {} files. {} files have been skipped due to errors, " "{} were successfully processed and {} already existed.\n").format( total, errors, oks, skipped_count) print(summary) # generate log if generate_log: log = [summary] + log_lines with open(os.path.join(output_dir, 'log'), 'w') as f: f.write('\n'.join(log))
def compute(audio, pool, options): INFO('Computing Tonal descriptors...') sampleRate = options['sampleRate'] frameSize = options['frameSize'] hopSize = options['hopSize'] zeroPadding = options['zeroPadding'] windowType = options['windowType'] frames = essentia.FrameGenerator(audio=audio, frameSize=frameSize, hopSize=hopSize) window = essentia.Windowing(size=frameSize, zeroPadding=zeroPadding, type=windowType) spectrum = essentia.Spectrum(size=(frameSize + zeroPadding) / 2) spectral_peaks = essentia.SpectralPeaks(maxPeaks=10000, magnitudeThreshold=0.00001, minFrequency=40, maxFrequency=5000, orderBy="frequency") tuning = essentia.TuningFrequency() # computing the tuning frequency tuning_frequency = 440.0 for frame in frames: frame_windowed = window(frame) frame_spectrum = spectrum(frame_windowed) (frame_frequencies, frame_magnitudes) = spectral_peaks(frame_spectrum) #if len(frame_frequencies) > 0: (tuning_frequency, tuning_cents) = tuning(frame_frequencies, frame_magnitudes) pool.add(namespace + '.' + 'tuning_frequency', tuning_frequency) #, pool.GlobalScope) # computing the HPCPs spectral_whitening = essentia.SpectralWhitening() hpcp_key_size = 36 hpcp_chord_size = 36 hpcp_tuning_size = 120 hpcp_key = essentia.HPCP(size=hpcp_key_size, referenceFrequency=tuning_frequency, bandPreset=False, minFrequency=40.0, maxFrequency=5000.0, weightType='squaredCosine', nonLinear=False, windowSize=4.0 / 3.0, sampleRate=sampleRate) hpcp_chord = essentia.HPCP(size=hpcp_chord_size, referenceFrequency=tuning_frequency, harmonics=8, bandPreset=True, minFrequency=40.0, maxFrequency=5000.0, splitFrequency=500.0, weightType='cosine', nonLinear=True, windowSize=0.5, sampleRate=sampleRate) hpcp_tuning = essentia.HPCP(size=hpcp_tuning_size, referenceFrequency=tuning_frequency, harmonics=8, bandPreset=True, minFrequency=40.0, maxFrequency=5000.0, splitFrequency=500.0, weightType='cosine', nonLinear=True, windowSize=0.5, sampleRate=sampleRate) # intializing the HPCP arrays hpcps_key = [] hpcps_chord = [] hpcps_tuning = [] # computing HPCP loop frames = essentia.FrameGenerator(audio=audio, frameSize=frameSize, hopSize=hopSize) total_frames = frames.num_frames() n_frames = 0 start_of_frame = -frameSize * 0.5 progress = Progress(total=total_frames) for frame in frames: #frameScope = [ start_of_frame / sampleRate, (start_of_frame + frameSize) / sampleRate ] #pool.setCurrentScope(frameScope) if options['skipSilence'] and essentia.isSilent(frame): total_frames -= 1 start_of_frame += hopSize continue frame_windowed = window(frame) frame_spectrum = spectrum(frame_windowed) # spectral peaks (frame_frequencies, frame_magnitudes) = spectral_peaks(frame_spectrum) if (len(frame_frequencies) > 0): # spectral_whitening frame_magnitudes_white = spectral_whitening( frame_spectrum, frame_frequencies, frame_magnitudes) frame_hpcp_key = hpcp_key(frame_frequencies, frame_magnitudes_white) frame_hpcp_chord = hpcp_chord(frame_frequencies, frame_magnitudes_white) frame_hpcp_tuning = hpcp_tuning(frame_frequencies, frame_magnitudes_white) else: frame_hpcp_key = essentia.array([0] * hpcp_key_size) frame_hpcp_chord = essentia.array([0] * hpcp_chord_size) frame_hpcp_tuning = essentia.array([0] * hpcp_tuning_size) # key HPCP hpcps_key.append(frame_hpcp_key) # add HPCP to the pool pool.add(namespace + '.' + 'hpcp', frame_hpcp_key) # chords HPCP hpcps_chord.append(frame_hpcp_chord) # tuning system HPCP hpcps_tuning.append(frame_hpcp_tuning) # display of progress report progress.update(n_frames) n_frames += 1 start_of_frame += hopSize progress.finish() # check if silent file if len(hpcps_key) == 0: raise EssentiaError('This is a silent file!') # key detection key_detector = essentia.Key(profileType='temperley') average_hpcps_key = numpy.average(essentia.array(hpcps_key), axis=0) average_hpcps_key = normalize(average_hpcps_key) # thpcps max_arg = numpy.argmax(average_hpcps_key) thpcp = [] for i in range(max_arg, len(average_hpcps_key)): thpcp.append(float(average_hpcps_key[i])) for i in range(max_arg): thpcp.append(float(average_hpcps_key[i])) pool.add(namespace + '.' + 'thpcp', thpcp) #, pool.GlobalScope ) (key, scale, key_strength, first_to_second_relative_strength) = key_detector( essentia.array(average_hpcps_key)) pool.add(namespace + '.' + 'key_key', key) #, pool.GlobalScope) pool.add(namespace + '.' + 'key_scale', scale) #, pool.GlobalScope) pool.add(namespace + '.' + 'key_strength', key_strength) #, pool.GlobalScope) # chord detection chord_detector = essentia.Key(profileType='tonictriad', usePolyphony=False) hpcp_frameSize = 2.0 # 2 seconds hpcp_number = int(hpcp_frameSize * (sampleRate / hopSize - 1)) for hpcp_index in range(len(hpcps_chord)): hpcp_index_begin = max(0, hpcp_index - hpcp_number) hpcp_index_end = min(hpcp_index + hpcp_number, len(hpcps_chord)) average_hpcps_chord = numpy.average(essentia.array( hpcps_chord[hpcp_index_begin:hpcp_index_end]), axis=0) average_hpcps_chord = normalize(average_hpcps_chord) (key, scale, strength, first_to_second_relative_strength) = chord_detector( essentia.array(average_hpcps_chord)) if scale == 'minor': chord = key + 'm' else: chord = key frame_second_scope = [ hpcp_index_begin * hopSize / sampleRate, hpcp_index_end * hopSize / sampleRate ] pool.add(namespace + '.' + 'chords_progression', chord) #, frame_second_scope) pool.add(namespace + '.' + 'chords_strength', strength) #, frame_second_scope) # tuning system features keydetector = essentia.Key(profileType='diatonic') average_hpcps_tuning = numpy.average(essentia.array(hpcps_tuning), axis=0) average_hpcps_tuning = normalize(average_hpcps_tuning) (key, scale, diatonic_strength, first_to_second_relative_strength) = keydetector( essentia.array(average_hpcps_tuning)) pool.add(namespace + '.' + 'tuning_diatonic_strength', diatonic_strength) #, pool.GlobalScope) (equal_tempered_deviation, nontempered_energy_ratio, nontempered_peaks_energy_ratio ) = essentia.HighResolutionFeatures()(average_hpcps_tuning) pool.add(namespace + '.' + 'tuning_equal_tempered_deviation', equal_tempered_deviation) #, pool.GlobalScope) pool.add(namespace + '.' + 'tuning_nontempered_energy_ratio', nontempered_energy_ratio) #, pool.GlobalScope) pool.add(namespace + '.' + 'tuning_nontempered_peaks_energy_ratio', nontempered_peaks_energy_ratio) #, pool.GlobalScope)
def aggregate_descriptors(self, descriptors_stats={}): aggregated = {} for namespace in self.descriptors: aggregated[namespace] = {} descs = self.descriptors[namespace].keys() descs.sort() stats_default = ['mean', 'var', 'min', 'max'] for desc in descs: values = self.descriptors[namespace][desc]['values'] if (namespace, desc) in self.statsBlackList: # make sure there is only one value if len(values) != 1: raise EssentiaError( 'You declared %s as a global descriptors, but there are more than 1 value: %s' % (desc, values)) value = values[0] try: # if value is numeric aggregated[namespace][desc] = { 'value': essentia.array(value) } except: # if value is not numeric aggregated[namespace][desc] = {'value': value} continue aggregated[namespace][desc] = {} aggrDesc = aggregated[namespace][desc] stats = list(stats_default) if not isinstance(values[0], numpy.ndarray): #stats += [ 'percentile_5', 'percentile_95' ] stats += ['dmean', 'dmean2', 'dvar', 'dvar2'] if namespace in descriptors_stats and desc in descriptors_stats[ namespace]: stats = descriptors_stats[namespace][desc] try: if 'mean' in stats: aggrDesc['mean'] = essentia.array( numpy.mean(values, axis=0)) if 'var' in stats: aggrDesc['var'] = essentia.array( numpy.var(values, axis=0)) if 'min' in stats: aggrDesc['min'] = essentia.array( numpy.min(values, axis=0)) if 'max' in stats: aggrDesc['max'] = essentia.array( numpy.max(values, axis=0)) derived = None derived2 = None if 'dmean' in stats: if not derived: derived = [ a - b for a, b in izip(values[1:], values[:-1]) ] aggrDesc['dmean'] = essentia.array( numpy.mean(numpy.abs(derived), axis=0)) if 'dvar' in stats: if not derived: derived = [ a - b for a, b in izip(values[1:], values[:-1]) ] aggrDesc['dvar'] = essentia.array( numpy.var(derived, axis=0)) if 'dmean2' in stats: if not derived: derived = [ a - b for a, b in izip(values[1:], values[:-1]) ] if not derived2: derived2 = [ a - b for a, b in izip(derived[1:], derived[:-1]) ] if derived2: aggrDesc['dmean2'] = essentia.array( numpy.mean(numpy.abs(derived2), axis=0)) else: aggrDesc['dmean2'] = 'undefined' if 'dvar2' in stats: if not derived: derived = [ a - b for a, b in izip(values[1:], values[:-1]) ] if not derived2: derived2 = [ a - b for a, b in izip(derived[1:], derived[:-1]) ] if derived2: aggrDesc['dvar2'] = essentia.array( numpy.var(derived2, axis=0)) else: aggrDesc['dvar2'] = 'undefined' if 'frames' in stats: aggrDesc['frames'] = essentia.array(values) if 'single_gaussian' in stats: single_gaussian = essentia.SingleGaussian() (m, cov, icov) = single_gaussian(essentia.array(values)) aggrDesc['mean'] = m aggrDesc['cov'] = cov aggrDesc['icov'] = icov for stat in stats: if stat.startswith('percentile_'): p = float(stat.split('_')[1]) aggrDesc[stat] = essentia.array( percentile(values, p)) except (TypeError, ValueError): # values are not numeric if len(values) == 1: aggrDesc['value'] = values[0] else: aggrDesc['value'] = [] for value in values: aggrDesc['value'].append(value) return aggregated