Lossy czy Lossless?

18

Biorąc pod uwagę plik audio, określ, czy jest on zakodowany w formacie stratnym czy bezstratnym. Do celów tego wyzwania należy klasyfikować tylko następujące formaty:

Zasady

  • Jeśli dane wejściowe są przyjmowane w postaci nazwy pliku, nie należy przyjmować żadnych założeń dotyczących nazwy pliku (np. Nie gwarantuje się, że rozszerzenie jest poprawne dla formatu, a nawet jest obecne).
  • W plikach wejściowych nie będą obecne metadane ID3 ani APEv2.
  • Można zastosować dowolne dwa unikalne i rozróżnialne dane wyjściowe, takie jak 0i 1, lossyi lossless, fooi baritp.

Przypadki testowe

Przypadki testowe dla tego wyzwania składają się z pliku zip znajdującego się tutaj, który zawiera dwa katalogi: lossyi lossless. Każdy katalog zawiera kilka plików audio, które są 0,5-sekundowymi falami sinusoidalnymi 440 Hz, zakodowanymi w różnych formatach. Wszystkie pliki audio mają rozszerzenia pasujące do powyższych formatów, z wyjątkiem A440.m4a(którym jest audio AAC w kontenerze MPEG Layer 4).

Mego
źródło
Dźwięk AAC w kontenerze MPEG Layer 4 ” rodzi pytanie: jakie inne formaty kontenerów muszą obsługiwać odpowiedzi?
Peter Taylor
@PeterTaylor Tylko AAC otrzymało specjalną wzmiankę, ponieważ nie mogłem znaleźć sposobu na dostarczenie dźwięku AAC bez osadzenia go w kontenerze MPEG Layer 4 przez FFMPEG. Dźwięk Vorbis jest osadzony w pojemniku Ogg (co jest normą dla dźwięku Vorbis). Wszystkie pozostałe są samodzielnymi formatami.
Mego
Czy jesteś pewien pliku TTA? Zgodnie ze specyfikacją pliki TTA powinny zaczynać się od magicznej liczby TTA1 lub TTA2. FFM2 (magiczna liczba twojego pliku) wydaje się odpowiadać strumieniowi FFmpeg. Plik Linux rozpoznaje nagłówek TTA1, ale nie FFM2.
Dennis
Czy możemy również założyć, że AAC zawsze będzie w nagłówku MPEG Layer 4? Jeśli nie, to co może założymy?
Dennis
Czy możemy wziąć zawartość pliku jako dane wejściowe, czy też nasz kod musi je odzyskać?
Kudłaty

Odpowiedzi:

18

Galaretka , 7 5 bajtów

ƈƈeØA

Formaty stratne zwracają 0 , formaty bezstratne zwracają 1 .

Wypróbuj online! (permalinki w Gist)

tło

Formaty, które musimy obsługiwać, mają następujące magiczne liczby, tzn. Zaczynają się od tych bajtów.

Format    Header (text)       Header (hex)
-----------------------------------------------------------------------------------
AC3       .w                  0B 77
AMR       #!AMR               23 21 41 4D 52
AAC       ÿñP@..ü             FF F1 50 40 00 1F FC
  M4A     ... ftypM4A         00 00 00 20 66 74 79 70 4D 34 41 20
MP2       ÿû                  FF FB
MP3       ÿû                  FF FB
OGG       OggS                4F 67 67 53
WMA       0&²u.fÏ.¦Ù.ª.bÎl    30 26 B2 75 8E 66 CF 11 A6 D9 00 AA 00 62 CE 6C

AIFF      FORM????AIFF        46 4F 52 4D ?? ?? ?? ?? 41 49 46 46
FLAC      fLaC                66 4C 61 43
TTA       TTA1                54 54 41 31
  FFM2    FFM2                46 46 4D 32
WAV       RIFF????WAVE        52 49 46 46 ?? ?? ?? ?? 57 41 56 45

Wcięte wpisy to kontenery dla poprzedniego formatu, które pojawiają się w przypadkach testowych. ?oznacza bajt zmienny. .oznacza bajt niedrukowalny. Wszystkie pozostałe bajty są wyświetlane jako znak ISO 8859-1.

Patrząc tylko na drugi bajt, możemy łatwo ustalić format:

Formaty bezstratne mają jako drugi bajt wielką literę, natomiast formaty stratne nie.

Jak to działa

ƈƈeØA  Main link. No arguments.

ƈ      Read a char from STDIN and set the left argument to this character.
 ƈ     Read another char from STDIN and set the return value to this character.
   ØA  Yield the uppercase alphabet, i.e., "ABCDEFGHIJKLMNOPQRSTUVWXYZ".
  e    Exists; return 1 if the return value (second char on STDIN) belongs to the
       uppercase alphabet, 0 if not.
Dennis
źródło
2
To bardzo sprytne rozwiązanie.
Mego
10

C, 82 80 32 bajty

Zainspirowany odpowiedzią @Dennis , można to znacznie zmniejszyć:

main(){return getchar()&200^64;}

Prześlij dane pliku do standardowego wejścia. Zwraca 0 dla stratnych lub niezerowe dla stratnych.

Lub oryginał dłużej czek:

char v[5];main(){scanf("%4c",v);return*v&&strstr("fLaC FORM RIFF TTA1 FFM2",v);}

Prześlij dane pliku do standardowego wejścia. Zwraca niezerową (1) dla stratnej lub 0 dla stratnej.

Z tego, co mogę powiedzieć, wszystkie wymienione formaty mają osobne magiczne liczby (oprócz AIFF / WAV, ale i tak oba są bezstratne), więc to po prostu sprawdza tę magiczną liczbę pod kątem znanej bezstratnej wartości. Ma *v&&to na celu ochronę przed pasującymi plikami, które zaczynają się od bajtu zerowego (M4A).

Uwzględniłem wartości, które znalazłem w arkuszach specyfikacji ( fLaC= FLAC, RIFF= WAV / AIFF, TTA1= TTA), a FORM= AIFF i FFM2= TTA pochodzą z dostarczonych plików przykładowych (mogę tylko zgadywać, że są to formaty opakowań lub nowsze wersje).


Lub krótsza alternatywa przypominająca oszustwo:

Plik Bash +, 61 bajtów

N="$(file "$1")";[[ $N = *": d"* || $N = *IF* || $N = *FL* ]]

Traktuje nazwę pliku jako argument. Zwraca 0 dla stratnych lub niezerowe dla stratnych.

Robi dokładnie to, czego można oczekiwać; pyta, filejaki jest typ pliku, a następnie sprawdza znane wzorce. Dopasowania TTA : d( : data), dopasowanie AIFF / WAV IFi dopasowania FLAC FL. Żaden z bezstratnych wyników nie pasuje do żadnego z nich, a ja przetestowałem, że nadal działa, jeśli nazwy plików zostaną usunięte.


Testowanie:

for f in "$@"; do
    echo "Checking $f:";
    ./identify2 "$f" && echo "shorter C says LOSSLESS" || echo "shorter C says LOSSY";
    ./identify < "$f" && echo "longer C says LOSSY" || echo "longer C says LOSSLESS";
    ./identify.sh "$f" && echo "file says LOSSLESS" || echo "file says LOSSY";
done;

# This can be invoked to test all files at once with:
./identify_all.sh */*
Dave
źródło
Czy Twoje rozwiązanie Bash działa również, jeśli rozszerzenie pliku jest nieprawidłowe? „nie gwarantuje się, że rozszerzenie jest poprawne dla formatu”, więc powinieneś mieć możliwość nadania plikowi niewłaściwego rozszerzenia i nadal działać.
mbomb007
@ mbomb007 Właśnie przetestowałem z pomieszanymi rozszerzeniami i nadal dobrze je identyfikuję. Myślę, że filenie ufa rozszerzeń anyway (wielu użytkowników rzecz zmiany nazwy PNG do JPEG jest taka sama, jak przekształcenie go!)
Dave
7

GS2 , 3 bajty

◄5ì

Formaty stratne zwracają 0 , formaty bezstratne zwracają 1 .

Wypróbuj online! (permalinki w Gist)

tło

Formaty, które musimy obsługiwać, mają następujące magiczne liczby, tzn. Zaczynają się od tych bajtów.

Format    Header (text)       Header (hex)
-----------------------------------------------------------------------------------
AC3       .w                  0B 77
AMR       #!AMR               23 21 41 4D 52
AAC       ÿñP@..ü             FF F1 50 40 00 1F FC
  M4A     ... ftypM4A         00 00 00 20 66 74 79 70 4D 34 41 20
MP2       ÿû                  FF FB
MP3       ÿû                  FF FB
OGG       OggS                4F 67 67 53
WMA       0&²u.fÏ.¦Ù.ª.bÎl    30 26 B2 75 8E 66 CF 11 A6 D9 00 AA 00 62 CE 6C

AIFF      FORM????AIFF        46 4F 52 4D ?? ?? ?? ?? 41 49 46 46
FLAC      fLaC                66 4C 61 43
TTA       TTA1                54 54 41 31
  FFM2    FFM2                46 46 4D 32
WAV       RIFF????WAVE        52 49 46 46 ?? ?? ?? ?? 57 41 56 45

Wcięte wpisy to kontenery dla poprzedniego formatu, które pojawiają się w przypadkach testowych. ?oznacza bajt zmienny. .oznacza bajt niedrukowalny. Wszystkie pozostałe bajty są wyświetlane jako znak ISO 8859-1.

Patrząc tylko na drugi bajt, możemy łatwo ustalić format:

Formaty bezstratne mają jako drugi bajt wielką literę, natomiast formaty stratne nie.

Jak to działa

     (implcit) Push the entire input from STDIN as a string on the stack.
◄    Push 1.
 5   Get the strings character at index 1, i.e., its second character.
  ì  Test if the character is an uppercase letter.
Dennis
źródło
2

JavaScript (ES6), 20 bajtów

c=>/^[fFRT]/.test(c)

Wyjaśnienie

Bierze zawartość pliku jako wejście i powrót true, jeśli plik jest bezstratna lub falsejeśli jest stratny testując pierwszy znak tego wejścia, aby zobaczyć, czy jest to f, F, Rlub T.


Spróbuj

Wklej zawartość pliku do textarea.

f=
c=>/^[fFRT]/.test(c)
i.addEventListener("input",_=>console.log(f(i.value)))
<textarea id=i></textarea>


Drugi wysiłek, 81 63 bajtów

Pobiera zawartość pliku z podanego adresu URL, który okazał się przesadą.

u=>fetch(u).then(r=>r.text()).then(t=>alert(/^[fFRT]/.test(t)))

Pierwszy wysiłek, 146 116 89 bajtów

Niepoprawne, ponieważ typy MIME są powiązane z rozszerzeniami i, najwyraźniej, nagłówki odpowiedzi kwalifikują się jako dodatkowe dane wejściowe.

u=>fetch(u).then(r=>alert(/aiff|flac|tta|wave|wav$/.test(r.headers.get("Content-Type"))))
Kudłaty
źródło
serwery sieciowe zwykle generują MIME na podstawie rozszerzenia pliku, co jest niezgodne z regułami tutaj. Czy sprawdziłeś, czy działa na plikach bez rozszerzenia? (jeśli tak, to prawdopodobnie należy podać nazwę serwera, którego używasz jako część „języka”)
Dave
1
@Dave Jestem pewien, że nie. MIME i rozszerzenie nie są w ogóle od siebie zależne. Jeśli zmienisz rozszerzenie pliku i prześlesz go, typ MIME to MIME faktycznej zawartości pliku, a nie rozszerzenie. W tej chwili przyjmowanie danych wejściowych jako adresu URL prawdopodobnie nie jest dozwolone. Nie jestem pewny.
mbomb007
@ mbomb007 Nie jestem pewien, dlaczego to mówisz; typy mime są rzeczą internetową, a nie systemem plików / plików, a znane mi serwery określą ją na podstawie rozszerzenia przy użyciu skonfigurowanego wyszukiwania (dla szybkości wyświetlania nagłówków; nie chcą sprawdzać każdego pliku przed podaniem to). Weźmy na przykład Apache AddType <mime> <extension>lub IIS <MimeMap>. Oczywiście konkretna konfiguracja lub narzędzie do hostowania plików może przeprowadzić odpowiednią inspekcję, a to zasługiwałoby na wybór serwera jako części odpowiedzi (ponieważ to serwer określa typ pliku!)
Dave
1
Sprawdziłem poprawność plików w .NET, a typ MIME pasował do zawartości, nawet jeśli rozszerzenie zostało zmienione przed przesłaniem.
mbomb007
@ mbomb007, którykolwiek z używanych komponentów .NET musiał przeprowadzić inspekcję pliku podczas przesyłania lub podczas udostępniania plików (domyślam się, że podczas przesyłania wydajność, ale nigdy nie wiadomo). Wracając do mojego oryginalnego komentarza, to uczyniłoby z tej odpowiedzi coś w rodzaju „JavaScript + .NET SeverLibraryXYZ”. Jeśli chodzi o pobieranie danych wejściowych z adresu URL, rozumiem, dlaczego się wahasz, ale osobiście uważam, że jest to ważne, o ile wspomniany jest wybór serwera. Może jest na tym meta, ale ostatecznie to zależy od Mego.
Dave
1

Chip , 11 bajtów

~Z~S
t'G~aF

Bezwstydnie odwzorowała galaretkową odpowiedź Dennisa w Chipie.

Bezstratne zwroty 0x0, straty zwrotne 0x1.

Wypróbuj online , linki w skrócie (dzięki Dennis za strategię TIO tutaj)

Wyjaśnić!

~Z~S
t'

Ta część to sprzątanie: to S kipuje pierwszy bajt i tkończy się po drugim.

G~aF

To jest sedno decyzji. Do każdego bajtu wejściowego mają dostęp bity HGFEDCBA. GdybyG jest ustawiona, a Fnie jest, oznacza to, że bajt jest w zakresie 0x40do 0x5f(co jest mniej więcej odpowiednikiem „dużą literą” i wystarczająco dobre dla zadania pod ręką).

Jednak dla oszczędności bajtów odwracam tę decyzję od G and (not F)na (not G) or F, ponieważ lub może być ukryta w Chipie.

Ta wynikowa wartość prawda / fałsz jest następnie umieszczana w a, który jest najniższym bitem wyniku. (Wszystkie inne bity będą wynosić zero). W TIO uruchamiam dane wyjściowe przez zrzut heksowy, aby wartości były widoczne.

Równolegle w języku C można by powiedzieć coś takiego:

out_byte = !(in_byte & 0x40) && (in_byte & 0x20)
Phlarx
źródło
1

Cubix, 16 bajtów

$-!u'HIa'@/1@O<

Formularz netto:

    $ -
    ! u
' H I a ' @ / 1
@ O < . . . . .
    . .
    . .

Spróbuj sam

Należy wprowadzić wartości bajtów dziesiętnych pliku na oddzielnej liście. Separator nie ma znaczenia, wystarczy coś, co nie jest cyfrą ani znakiem minus. Kod naprawdę obchodzi tylko pierwszy bajt, więc możesz pominąć resztę pliku, jeśli chcesz. Program generuje dla stratnych 0i bezstratnych 1. Wypróbuj tutaj ! Domyślne wejście używa nagłówka FLAC.

Wyjaśnienie

Zaletą plików jest to, że (prawie) wszystkie mają tak zwaną magię. To kilka pierwszych bajtów pliku. Dobre oprogramowanie nie sprawdza rozszerzenia pliku, ale raczej magię pliku, aby sprawdzić, czy może obsłużyć określony plik.

Dennis znalazł sposób na użycie tej magii do znalezienia rodzaju kompresji, ale fakt, że odrzucił pierwszy bajt, sprawił, że chciałem wymyślić metodę, która użyłaby pierwszego bajtu, a nie drugiego. W końcu ta społeczność polega na oszczędzaniu bajtów.

Oto lista pierwszych bajtów różnych typów plików. Podzieliłem je na dwie grupy: stratne i bezstratne. Oto wartości ich pierwszego bajtu w postaci dziesiętnej, szesnastkowej i binarnej. Możesz już zobaczyć wzór ...

Lossy:                  Lossless:
255:0xFF:0b11111111     102:0x66:0b01100110
 79:0x4F:0b01001111      84:0x54:0b01010100
 35:0x23:0b00100011      82:0x52:0b01010010
 11:0x0B:0b00001011      70:0x46:0b01000110
  0:0x00:0b00000000

Wzorzec, który widziałem, był taki, że drugi bit (liczony od lewej do prawej) zawsze był włączony w „bezstratnych” bajtach, a piąty bit był zawsze wyłączony. Ta kombinacja nie pojawia się w żadnym z formatów stratnych. Aby to „wyodrębnić”, po prostu zrobilibyśmy binarne AND (by 0b01001000 (=72)), a następnie porównaliśmy z 0b01000000 (=64). Jeśli oba są równe, format wejściowy jest bezstratny, w przeciwnym razie jest stratny.

Niestety Cubix nie ma takiego operatora porównania, więc użyłem odejmowania (jeśli wynikiem jest 64, daje to 0, a w przeciwnym razie daje 8, -56 lub -64. Wrócę do tego później.

Najpierw zacznijmy od początku programu. Binarne AND odbywa się za pomocą apolecenia:

'HIa
'H   # Push 0b01001000 (72)
  I  # Push input
   a # Push input&72

Następnie porównujemy do 64 za pomocą odejmowania (zauważmy, że uderzamy w lustro, które odbija IP do górnej powierzchni [pierwsza linia, drugi znak, skierowany na południe] w środku tej części).

'@-
'@  # Push 0b01000000 (64)
  - # Subtract from (input&72)
    # Yields 0 for lossy, non-zero otherwise

Po odwróceniu adresu IP przez u, używamy przepływu sterującego, aby wypchnąć a 1na stos, jeśli (i tylko jeśli) góra stosu jest różna od zera:

!$1
!   # if top = 0:
 $1 #   do nothing
    # else:
  1 #   push 1

Po owinięciu sześcianu uderzamy w <instrukcję, która wskazuje IP na zachód w czwartej linii. Wszystko, co pozostaje do zrobienia, to wyjście i zakończenie.

O@
O  # Output top of the stack as number
 @ # End program

Tak więc program generuje dane 0bezstratne i 1stratne.

Łukasz
źródło