Chcę zgrać / zarchiwizować całą moją kolekcję muzyki do bezstratnego, ale skompresowanego formatu pliku, tj. Wszystkie pliki powinny być idealne , bezstratne reprezentacje oryginalnych danych, ale powinny zajmować mniej miejsca niż nieskompresowany WAV (E).
WAV (E) jest niedopuszczalny, ponieważ jest niewolny (zastrzeżony materiał firmy Microsoft), kompresja między platformami jest uciążliwa lub niemożliwa, a rozmiar pliku jest ograniczona do 4 GB. Dlatego wybieram FLAC (Free Lossless Audio Codec).
Ponieważ digitalizacja całej kolekcji jest gigantycznym zadaniem, a FLAC oferuje 9 poziomów kompresji (od 0 do 8), pojawia się złote pytanie :
Który poziom kompresji powinienem mądrze wybrać?
Komentarze
- to pytanie nie ' w ogóle nie dotyczy projektowania dźwięku, ale dotyka wyboru, przed którym stają niektórzy projektanci dźwięku, czyli: jak najlepiej radzić sobie z naszymi stale rosnącymi bibliotekami nagrań. Osobiście ' wybieram FLAC zamiast WAVE tylko z powodu problemu z pamięcią masową, ale ' Obawiam się, że nie ' nie mam wglądu w poziom kompresji.
- Co ciekawe, opublikowałem najpierw w Muzyka , ale tamtejsi ludzie zalecili przeniesienie go do Sound Design.
Odpowiedź
Poziomy kompresji FLAC są (tylko) wymianą pomiędzy czasem kodowania a rozmiar pliku . Czas dekodowania jest prawie niezależny od stopnia kompresji. W dalszej części będę nazywać poziomy kompresji 0, …, 8 jako FLAC-0, …, FLAC-8.
W skrócie : polecam FLAC-4 !
Proste rozwiązania
Oczywiście:
-
Jeśli nie obchodzi mnie czas kodowania, a miejsce to pieniądz, wybieram najwyższy poziom kompresji FLAC-8 .
-
Jeśli nie obchodzi mnie ilość miejsca, ale chcę jak najszybciej za tym wyjść, wybieram najniższy poziom kompresji FLAC-0 .
Trudne rozwiązanie
Gdzie jest pomiędzy rozmiarem pliku a czasem kodowania? Natknąłem się na artykuł Nathana Zacharyego dotyczący tego pytania, ale on porównuje tylko dwa pliki, koduje je tylko raz (czas kodowania bardzo się różni w zależności od obciążenia bocznego komputera ), a tabele są trudne do odczytania w porównaniu z wykresami.
Zainspirowany tym, przerobiłem jego pomiary na pięć kompletnych albumów każdy z innego gatunku i zakodował każdy plik / ścieżkę 10 razy .
Procedura:
- Zgraj album z
abcde
i właściwecdparanoia
ustawienia na nieskompresowany WAV. - Przekonwertuj każdy plik 10 razy dla każdego poziomu kompresji (FLAC-0 na FLAC-8) i weź średni czas kodowania w stosunku do FLAC-0 i rozmiar pliku w stosunku do F LAC-0 .
- W tym celu wyłączyłem połączenie internetowe, wszystkie okresowe zadania (
cronjobs
) i prawie wszystko inne, aby naprawdę działała głównie kompresja i jak najmniej zakłóceń.
- W tym celu wyłączyłem połączenie internetowe, wszystkie okresowe zadania (
Miara ta powinna być prawie niezależna od używanego sprzętu. Użyłem flaca w wersji 1.3.2 w Arch Linux przy użyciu flac <infile> --compression-level-X -f -o flacX.flac
.
Wydajność
Jeśli pomnożymy rozmiar względny z względnym czasem kodowania / kompresji otrzymasz wartość za zło . Ale ponieważ ta zła jest głównie rządzona przez względny czas, wykresy w dużym stopniu się pokrywają. Aby więc wyczyścić wykres, po prostu odwzorowałem zło w dobro , nazywam tutaj wydajnością .
Wyniki
Począwszy od FLAC-4, czas kompresji eksploduje, ALE są dwie niespodzianki:
-
Istnieje znaczne zmniejszenie rozmiaru pliku między FLAC-3 a FLAC-4 w zależności od gatunku muzycznego: Muzyka klasyczna ma znacznie niższą kompresję przy użyciu FLAC-4. Zakładam, że dzieje się tak, ponieważ FLAC używa liniowego modelu predykcji kompresji, który działa gorzej w przypadku bardziej złożonej (mniej liniowej) muzyki.
-
W przypadku muzyki nieklasycznej FLAC-3 jest nawet znacznie gorszy niż FLAC-2 pod względem rozmiaru pliku.
Zalecenia
Zalecam używanie poziomu kompresji FLAC-4 .
Przejście wyżej znacząco wydłuża czas kodowania z marginalną poprawą redukcji rozmiaru pliku (średnie zmniejszenie z FLAC-4 do FLAC-8 w tym teście to 1,2% z 182% wzrost średniego czasu kompresji).
Dodatek
Albumy
Właśnie wziąłem pierwsze pięć losowych płyt CD (wymienione poniżej) o których myślałem, że reprezentują inną dziedzinę muzyki. Linki celowo prowadzą do Amazon, aby zapewnić łatwą możliwość wglądu w muzykę / w celu zorientowania się w muzyce, ponieważ ma to znaczący wpływ na kompresję.
- Emil Gilels – Beethoven: Sonaty fortepianowe nr 21 „Waldstein”, 26 „Les Adieux” & 23 „Appassionata” ( classic)
- Karl Jenkins – The Armed Man – A Mass for Peace (classic, mass)
- Unni Wilhelmsen – 7 (pop, jazz, folk)
- Alin Coen Band – We ” re Not the Ones We Think We Were (indie, folk, piosenkarz i autor tekstów)
- Bukahara – Bukahara Trio ( neofolk , balkanfolk )
Program
Do tego zadania napisałem python
program, który przechodzi przez wszystkie podfoldery (albumy) w danym folderze (), aby przetestować wszystkie pliki .wav i pogrupować / wykreślić je według nazwy podfolderu.
<folder> Album 1 Album 2 ...
Analiza zostanie zapisana w pliku --outfile <file1>
. Aby wydrukować, użyj --infile <file1>
i --outfile <file2>
.
#!/usr/bin/python3 #encoding=utf8 import os, sys, subprocess, argparse from datetime import datetime, timedelta from os.path import isfile, isdir, join import numpy as np import matplotlib.pyplot as plt import pickle as pkl parser = argparse.ArgumentParser(description="Analyse flac compression and conversion time") group = parser.add_mutually_exclusive_group() group.add_argument("-d", "--directory", help="Input folder", type=str) group.add_argument("-if", "--infile", help="Plot saved stats (pickle file)", type=str) parser.add_argument("-of", "--outfile", help="Output file", type=str, required=True) parser.add_argument("-c", "--cycles", help="Number of cycles for each file", type=int, default=5) parser.add_argument("-C", "--maxcompression", help="Max compression level", type=int, default=8) args = parser.parse_args() args.maxcompression += 1 ############################################################ xlabel = "FLAC Compression Factor" ylabel_size = "Size Relative to FLAC-0" ylabel_time = "Mean Compression Time\nOver {} Cycles Relative to FLAC-0 [s]".format(args.cycles) ylabel_efficiency = r"Efficiency: $(-1)\cdot$ Fraction Time $\cdot$ Fraction Size $+ 2$" ############################################################ # Analyse and write mode if not args.infile: if isdir(args.directory): mypath = args.directory else: raise ValueError("Folder {} does not exist!".format(args.directory)) folders = [f for f in os.listdir(mypath) if isdir(join(mypath, f))] print("Found folders: {}".format(folders)) # Create temporary working folder temp_folder = "temp_{}".format(os.getpid()) if not os.path.exists(temp_folder): os.makedirs(temp_folder) # Every analysis will be storen in stats stats = {} remove = [] for folder in folders: stats[folder] = {} stats[folder]["files"] = [f for f in os.listdir(mypath+folder) if isfile(join(mypath+folder, f)) and f.endswith(".wav")] if len(stats[folder]["files"]) == 0: print("No .wav files found in {}. Skipping.".format(folder)) remove.append(folder) stats.pop(folder, None) else: stats[folder]["stats"] = np.empty([len(stats[folder]["files"]),args.maxcompression], dtype=object) # Remove empty (no .wav) folders from list for folder in remove: folders.remove(folder) totalfiles = [] for folder in folders: totalfiles += stats[folder]["files"] totalfiles = len(totalfiles) if totalfiles == 0: raise RuntimeError("No .wav files found!") totalcycles = totalfiles * args.cycles * args.maxcompression counter_cycles = 0 time_start = datetime.strptime(str(datetime.now()), "%Y-%m-%d %H:%M:%S.%f") for folder in folders: # i: 0..Nfiles # n: 0..8 files = stats[folder]["files"] for i in range(len(files)): infile = "{}/{}".format(mypath+folder,files[i]) for n in range(args.maxcompression): Dtime = [] for j in range(args.cycles): time1 = datetime.strptime(str(datetime.now()), "%Y-%m-%d %H:%M:%S.%f") subprocess.run(["flac", infile, "--compression-level-{}".format(n), "-f", "-o", "{}/flac{}.flac".format(temp_folder,n)]) time2 = datetime.strptime(str(datetime.now()), "%Y-%m-%d %H:%M:%S.%f") Dtime.append((time2-time1).total_seconds()) counter_cycles += 1 # Percentage of totalcycles status = counter_cycles/totalcycles remain_factor = (1 - status)/status time_current = datetime.strptime(str(datetime.now()), "%Y-%m-%d %H:%M:%S.%f") time_elapsed = (time_current - time_start).total_seconds() print("========================================") print("Status: {} %".format(int(100*status))) print("Estimated remaining time: {}".format(str(timedelta(seconds=int(remain_factor * time_elapsed))))) print("========================================") Dtime = np.mean(Dtime) size = os.path.getsize("{}/flac{}.flac".format(temp_folder,n)) # Array if size (regarded as constat) and mean compression time # (file1, FLAC0)(file1, FLAC1)...(file1, FLACmaxcompression) # (file2, FLAC0)(file2, FLAC1)...(file2, FLACmaxcompression) # ... stats[folder]["stats"][i,n] = (size, Dtime) for folder in folders: # Taking columnwise (for each compression level) means of size... stats[folder]["ploty_size"] = [np.mean([e[0] for e in stats[folder]["stats"][:,col]]) for col in range(np.shape(stats[folder]["stats"])[1])] # (relative to FLAC-0) stats[folder]["ploty_size"] = [i/stats[folder]["ploty_size"][0] for i in stats[folder]["ploty_size"]] # ... and mean time. stats[folder]["ploty_time"] = [np.mean([e[1] for e in stats[folder]["stats"][:,col]]) for col in range(np.shape(stats[folder]["stats"])[1])] # (relative to FLAC-0) stats[folder]["ploty_time"] = [i/stats[folder]["ploty_time"][0] for i in stats[folder]["ploty_time"]] # Rough "effectivity" estimation -size*time + 2 # Expl.: Starts at (0,1), therefore flipping with (-1) requires # + 2. Without (-1) would be "badness" stats[folder]["ploty_eff"] = [ 2 + (-1) * stats[folder]["ploty_size"][i] * stats[folder]["ploty_time"][i] for i in range(len(stats[folder]["ploty_size"]))] with open(args.outfile, "wb") as of: data = {} data["stats"] = stats data["folders"] = folders data["cycles"] = args.cycles data["maxcompression"] = args.maxcompression pkl.dump(data, of, protocol=pkl.HIGHEST_PROTOCOL) if os.path.isdir(temp_folder): subprocess.run(["rm", "-r", temp_folder]) else: with open(args.infile, "rb") as f: data = pkl.load(f) stats = data["stats"] folders = data["folders"] args.maxcompression = data["maxcompression"] args.cycles = data["cycles"] fig = plt.figure() plotx = range(args.maxcompression) pos = range(len(plotx)) ax_size = fig.add_subplot(111) ax_size.set_xticks(pos) ax_size.set_xticklabels(plotx) ax_size.set_title("FLAC compression comparison") ax_time = ax_size.twinx() ax_efficiency = ax_size.twinx() colorfracs = [i / (len(folders)-0.9) if i > 0 else 0 for i in range(len(folders))] # Actual plotting lns = [] for cfrac, folder in zip(colorfracs, folders): color = plt.cm.viridis(cfrac) l_size, = ax_size.plot(plotx, stats[folder]["ploty_size"], color=color, linestyle=":", label="Size Ratio: {}".format(folder)) l_time, = ax_time.plot(plotx, stats[folder]["ploty_time"], color=color, linestyle="--", label="Time Ratio: {}".format(folder)) l_eff, = ax_efficiency.plot(plotx, stats[folder]["ploty_eff"], color=color, linestyle="-", label="Efficiency: {}".format(folder)) lns.append(l_size) lns.append(l_time) lns.append(l_eff) ax_efficiency.spines["right"].set_position(("outward", 60)) ax_size.xaxis.grid(color=".85", linestyle="-", linewidth=.5) ax_size.set_xlabel(xlabel) ax_size.set_ylabel(ylabel_size) ax_efficiency.set_ylabel(ylabel_efficiency) ax_time.set_ylabel(ylabel_time) lgd = ax_time.legend(handles=lns, loc="upper center", bbox_to_anchor=(0.5, -.15), facecolor="#FFFFFF", prop={"family": "monospace","size": "small"}) fig.savefig(args.outfile, bbox_inches="tight", dpi=300)
Komentarze
- Hej … to niesamowita ilościowa analiza, którą tam zrobiłeś! Naprawdę doceniam, że poświęciłeś czas, aby to wszystko zrobić. Nie ' nie był szybki, ale naprawdę świetne wyniki. Dzięki!
Odpowiedź
Flac 0. Pamięć masowa jest obecnie tak tania, że wydaje mi się to nie do pomyślenia … również Flac 0 jest mniej podatny na czkawkę w wolniejszym systemie, ponieważ dekodowanie jest mniej wymagające.
Odpowiedź
W nawiązaniu do odpowiedzi Suuuehgi chciałbym również dodać, że jeśli zaczynasz od CD i zgrywanie bezpośrednio do formatu FLAC, czas kodowania może nie mieć żadnego znaczenia, ponieważ najpierw musisz zgrać muzykę, co zajmuje trochę czasu.
Oto, co próbowałem:
Używając programu dbPowerAmp CD Ripper, zgrałem moją kopię Mariah Carey „s " Merry Album świąteczny ". Zgrałem go raz na poziomie kompresji FLAC 8, raz na poziomie 5 (domyślnie dbPowerAmps) i raz na poziomie 0.
Oto total razy dla każdego zgrywania, od kliknięcia początku do zakończenia wszystkich plików FLAC:
Poziom 0 = 6:19
Poziom 5 = 6:18
Poziom 8 = 6:23
Jak widać, rozbieżność między wszystkimi trzema jest minimalna, w granicach < 5 sekund po sobie. Gdy widziałem, jak zgrywa i koduje, stan kodowania był tylko miganiem na ekranie, ledwo zarejestrowany. Obserwując zgrywanie systemu plików, wydawało się, że kodował on w locie, tak jak zgrał. YMMV jednak na wolniejszych systemach.
Jeśli chodzi o rozmiary plików, oto rozmiary wytwarzanych plików:
Poziom 0 = 278 MB
Poziom 5 = 257 MB
Poziom 8 = 256 MB
Podczas gdy łączne zgrywanie i kodowanie czasy ode były zasadniczo takie same, rozmiary plików nie były, JEDNAK zdecydowanie maleją zwroty na późniejszych poziomach kompresji (jak nawiązuje do odpowiedzi Suuuehgi).
Wydaje mi się, że jeśli zaczynają z płyt CD i mają przyzwoity komputer, czas potrzebny na zgrywanie i kodowanie nie zmieni się zbytnio w zależności od poziomu kompresji FLAC. Rozmiar pliku jednak się zmienia. Myślę, że sugestia dbPowerAmps o poziomie FLAC 5 jako domyślna jest dobra. Tylko 1 MB różnicy między FLAC 5 i FLAC 8, gdzie – tak jakbyś przeszedł na FLAC 0, mój przykład pokazuje 21 MB nadwyżki pamięci, którą można zapisać. To może nie wydawać się dużo, ale kiedy zgrywasz ogromne kolekcje, szybko się sumuje (pojedyncza piosenka FLAC może mieć mniej więcej ten rozmiar).
Zrobiono to na komputerze stacjonarnym z napędem USB 2 DVD , zgrywanie średnio z prędkością 7x. Specyfikacje mojego komputera stacjonarnego to procesor Intel Core i5-6500 o częstotliwości 3,2 GHz, 16 GB pamięci RAM i dysk SSD Samsung 860 EVO Sata.