forked from beefoo/media-tools
-
Notifications
You must be signed in to change notification settings - Fork 0
/
audio_to_samples.py
158 lines (130 loc) · 5.85 KB
/
audio_to_samples.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
# -*- coding: utf-8 -*-
# Looks for samples (clips) in arbitrary media based on audio
# python -W ignore audio_to_samples.py -in "media/downloads/vivaldi/*.mp3" -out "tmp/vivaldi.csv" -count 1296 -features 1 -sort "clarity=desc"
# python -W ignore audio_to_samples.py -in "E:/landscapes/downloads/ia_politicaladarchive/*.mp4" -out "tmp/ia_politicaladarchive_samples.csv" -count 5000 -features 1 -sort "clarity=desc=0.75&power=desc=0.75&dur=desc=0.75"
import argparse
import csv
from lib.audio_utils import *
from lib.collection_utils import *
from lib.io_utils import *
from lib.math_utils import *
from lib.processing_utils import *
import librosa
from multiprocessing import Pool
from multiprocessing.dummy import Pool as ThreadPool
import os
from os.path import join
import numpy as np
from pprint import pprint
import sys
# input
parser = argparse.ArgumentParser()
parser.add_argument('-in', dest="INPUT_FILE", default="media/sample/bird.wav", help="Input file pattern")
parser.add_argument('-dir', dest="MEDIA_DIRECTORY", default="media/downloads/ia_politicaladarchive/", help="Input dir")
parser.add_argument('-samples', dest="SAMPLES", default=-1, type=int, help="Max samples to produce per media file, -1 for all")
parser.add_argument('-min', dest="MIN_DUR", default=80, type=int, help="Minimum sample duration in ms")
parser.add_argument('-max', dest="MAX_DUR", default=1000, type=int, help="Maximum sample duration in ms, -1 for no max")
parser.add_argument('-delta', dest="ONSET_DELTA", default=0.07, type=float, help="Onset delta; must be larger than 0")
parser.add_argument('-out', dest="OUTPUT_FILE", default="tmp/samples.csv", help="CSV output file")
parser.add_argument('-overwrite', dest="OVERWRITE", action="store_true", help="Overwrite existing data?")
parser.add_argument('-threads', dest="THREADS", default=4, type=int, help="Number of concurrent threads, -1 for all available")
# arguments for managing large media sets
parser.add_argument('-features', dest="FEATURES", action="store_true", help="Retrieve features?")
parser.add_argument('-count', dest="COUNT", default=-1, type=int, help="Target total sample count, -1 for everything")
parser.add_argument('-filter', dest="FILTER", default="", help="Query string to filter by")
parser.add_argument('-sort', dest="SORT", default="", help="Query string to sort by")
args = parser.parse_args()
# Parse arguments
INPUT_FILE = args.INPUT_FILE
MEDIA_DIRECTORY = args.MEDIA_DIRECTORY
SAMPLES = args.SAMPLES
MIN_DUR = args.MIN_DUR
MAX_DUR = args.MAX_DUR
ONSET_DELTA = args.ONSET_DELTA
OUTPUT_FILE = args.OUTPUT_FILE
OVERWRITE = args.OVERWRITE
MULTIFILE_OUTPUT = ("%s" in OUTPUT_FILE)
FEATURES = args.FEATURES
COUNT = args.COUNT
FILTER = args.FILTER
SORT = args.SORT
# Audio config
FFT = 2048
HOP_LEN = int(FFT/4)
# Check if file exists already
# if os.path.isfile(OUTPUT_FILE) and not OVERWRITE:
# print("%s already exists. Skipping." % OUTPUT_FILE)
# sys.exit()
# Read files
fieldNames, files, fileCount = getFilesFromString(args)
if "duration" in fieldNames and "hasAudio" in fieldNames:
files = filterWhere(files, [("duration", 0, ">"), ("hasAudio", 0, ">")])
fileCount = len(files)
print("Found %s rows after filtering" % fileCount)
# Determine the number of samples per file
samplesPerFile = SAMPLES
if COUNT > 0 and fileCount > 0:
samplesPerFile = ceilInt(1.0 * COUNT / fileCount)
if samplesPerFile > 0:
print("%s samples per file." % samplesPerFile)
# Make sure output dirs exist
makeDirectories(OUTPUT_FILE)
# Get existing data
rows = []
if os.path.isfile(OUTPUT_FILE) and not OVERWRITE and not MULTIFILE_OUTPUT:
fieldNames, rows = readCsv(OUTPUT_FILE)
rowCount = len(rows)
# files = files[:1]
def getSamples(fn, sampleCount=-1):
print("Retrieving samples for %s..." % fn)
sampleData, y, sr = getAudioSamples(fn, min_dur=MIN_DUR, max_dur=MAX_DUR, fft=FFT, hop_length=HOP_LEN, delta=ONSET_DELTA)
print("Found %s samples in %s." % (len(sampleData), fn))
if len(sampleData) > 0:
# optionally retrieve features
if FEATURES:
sampleData = getFeaturesFromSamples(fn, sampleData, y=y, sr=sr)
# optionally, filter results
if len(FILTER) > 0:
sampleData = filterByQueryString(sampleData, FILTER)
# optionally sort results
if len(SORT) > 0:
sampleData = sortByQueryString(sampleData, SORT)
# if too many samples
if sampleCount > 0 and len(sampleData) > sampleCount:
sampleData = sampleData[:sampleCount]
return sampleData
headings = ["filename", "start", "dur"]
if FEATURES:
headings += ["power", "hz", "clarity", "note", "octave"]
totalCount = 0
progress = 0
def processFile(f):
global totalCount
global fileCount
global rowCount
global progress
global rows
global samplesPerFile
fn = f["filename"]
basename = os.path.basename(fn)
outputFilename = OUTPUT_FILE if not MULTIFILE_OUTPUT else OUTPUT_FILE % basename
if MULTIFILE_OUTPUT and not OVERWRITE and os.path.isfile(outputFilename):
print("Already found samples for %s. Skipping." % outputFilename)
# Check if we already have this data
elif not MULTIFILE_OUTPUT and not OVERWRITE and rowCount > 0 and len([row for row in rows if row["filename"]==basename]) > 0:
totalCount += len([row for row in rows if row["filename"]==fn])
print("Already found samples for %s. Skipping." % basename)
else:
result = getSamples(fn, samplesPerFile)
# Progressively save samples per audio file
append = (progress > 0 and not MULTIFILE_OUTPUT)
writeCsv(outputFilename, result, headings=headings, append=append)
totalCount += len(result)
progress += 1
printProgress(progress, fileCount)
print("Getting file samples...")
pool = ThreadPool(getThreadCount(args.THREADS))
results = pool.map(processFile, files)
pool.close()
pool.join()
print("%s samples in total." % totalCount)