Lossy o Lossless?


18

Dato un file audio, determinare se è codificato in un formato con perdita o in un formato senza perdita. Ai fini di questa sfida, devono essere classificati solo i seguenti formati:

Regole

  • Se l'input viene assunto sotto forma di un nome file, non si devono fare ipotesi sul nome file (ad esempio l'estensione non è garantita per essere corretta per il formato, o anche presente).
  • Non ci saranno metadati ID3 o APEv2 presenti nei file di input.
  • È possibile utilizzare due uscite univoche e distinguibili, come 0e 1, lossye lossless, fooe bar, ecc.

Casi test

I casi di test per questa sfida consistono in un file zip che si trova qui che contiene due directory: lossye lossless. Ogni directory contiene diversi file audio che sono tutte onde sinusoidali da 0,5 secondi a 440 Hz, codificate in vari formati. Tutti i file audio hanno estensioni corrispondenti ai formati sopra, ad eccezione di A440.m4a(che è audio AAC in un contenitore MPEG Layer 4).


" Audio AAC in un contenitore MPEG Layer 4 " solleva la domanda: quali altri formati di contenitore devono gestire le risposte?
Peter Taylor,

@PeterTaylor Solo AAC ha ricevuto una menzione speciale perché non sono riuscito a trovare un modo per fornire audio AAC senza incorporarlo in un contenitore MPEG Layer 4 tramite FFMPEG. L'audio Vorbis è incorporato in un contenitore Ogg (come è la norma per l'audio Vorbis). Tutti gli altri sono formati autonomi.
Mego

Sei sicuro del file TTA? Secondo le specifiche , i file TTA dovrebbero iniziare con il numero magico TTA1 o TTA2. FFM2 (il numero magico del tuo file) sembra corrispondere al flusso FFmpeg. Il file Linux riconosce l'intestazione TTA1, ma non quella FFM2.
Dennis,

Inoltre, possiamo supporre che AAC sarà sempre in un header MPEG Layer 4? Se no, che cosa possiamo assumiamo?
Dennis,

Possiamo prendere il contenuto del file come input o il nostro codice deve recuperarli?
Shaggy,

Risposte:


18

Gelatina , 7 5 byte

ƈƈeØA

I formati con perdita restituiscono 0 , i formati senza perdita restituiscono 1 .

Provalo online! (permalink in Gist)

sfondo

I formati che dobbiamo supportare hanno i seguenti numeri magici, ovvero iniziano con questi byte.

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

Le voci rientrate sono contenitori per il formato precedente che appaiono nei casi di test. ?indica un byte variabile. .indica un byte non stampabile. Tutti gli altri byte vengono visualizzati come caratteri ISO 8859-1.

Osservando solo il secondo byte, possiamo determinare il formato in modo semplice:

I formati senza perdita hanno una lettera maiuscola come secondo byte, mentre i formati con perdita non lo sono.

Come funziona

ƈƈ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.

2
Questa è una soluzione molto intelligente.
Mego,

10

C, 82 80 32 byte

Ispirato dalla risposta di @Dennis , questo può essere ulteriormente ridotto:

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

Pipa i dati del file su stdin. Restituisce 0 per lossless o diverso da zero per lossy.

O il controllo originale più lungo:

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

Pipa i dati del file su stdin. Restituisce diverso da zero (1) per lossless o 0 per lossy.

Da quello che posso dire, tutti i formati che hai elencato hanno numeri magici separati (tranne AIFF / WAV, ma comunque sono entrambi senza perdita), quindi questo controlla solo quel numero magico per un valore senza perdita noto. Il *v&&è solo per la protezione contro i file corrispondenti che iniziano con un byte nullo (M4A).

Ho incluso i valori che ho trovato nelle schede tecniche ( fLaC= FLAC, RIFF= WAV / AIFF, TTA1= TTA) e FORM= AIFF e FFM2= TTA provengono dai file di esempio forniti (posso solo immaginare che si tratti di formati wrapper o versioni successive).


O un'alternativa più breve di imbroglione:

Bash + file, 61 byte

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

Prende il nome file come argomento. Restituisce 0 per lossless o diverso da zero per lossy.

Fa esattamente quello che ti aspetteresti; chiede filequal è il tipo di file, quindi controlla i modelli noti. Partite TTA : d( : data), partita AIFF / WAV IFe partite FLAC FL. Nessuno dei risultati senza perdita corrisponde a nessuno di questi e ho testato che funziona ancora se i nomi dei file vengono rimossi.


test:

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 */*

La tua soluzione Bash funziona anche se l'estensione del file non è corretta? "l'estensione non è garantita per essere corretta per il formato", quindi dovresti essere in grado di dare a un file un'estensione errata e farlo funzionare ancora.
mbomb007,

@ mbomb007 Ho appena provato con le estensioni confuse e le identifica ancora bene. Penso che filecomunque non si fidi delle estensioni (molti utenti pensano che rinominare un png in un jpeg equivale a convertirlo!)
Dave,

7

GS2 , 3 byte

◄5ì

I formati con perdita restituiscono 0 , i formati senza perdita restituiscono 1 .

Provalo online! (permalink in Gist)

sfondo

I formati che dobbiamo supportare hanno i seguenti numeri magici, ovvero iniziano con questi byte.

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

Le voci rientrate sono contenitori per il formato precedente che appaiono nei casi di test. ?indica un byte variabile. .indica un byte non stampabile. Tutti gli altri byte vengono visualizzati come caratteri ISO 8859-1.

Osservando solo il secondo byte, possiamo determinare il formato in modo semplice:

I formati senza perdita hanno una lettera maiuscola come secondo byte, mentre i formati con perdita non lo sono.

Come funziona

     (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.

2

JavaScript (ES6), 20 byte

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

Spiegazione

Prende il contenuto del file come input e restituisce truese il file è senza perdita di dati o falsese si tratta di perdita di dati testando il primo carattere di tale ingresso a vedere se si tratta di un f, F, Ro T.


Provalo

Incolla il contenuto di un file nel file textarea.

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


Secondo sforzo, 81 63 byte

Recupera il contenuto di un file da un URL fornito, che si è rivelato eccessivo.

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

Primo sforzo, 146 116 89 byte

Non valido poiché i tipi mime sono legati alle estensioni e, apparentemente, le intestazioni di risposta si qualificano come input aggiuntivo.

u=>fetch(u).then(r=>alert(/aiff|flac|tta|wave|wav$/.test(r.headers.get("Content-Type"))))

i server Web di solito generano il MIME in base all'estensione del file, che è contro le regole qui. Hai controllato se funziona su file serviti senza estensione? (in tal caso dovresti probabilmente includere il nome del server che stai utilizzando come parte del "linguaggio")
Dave,

1
@Dave Abbastanza sicuro che non lo facciano. MIME ed estensione non dipendono affatto l'una dall'altra. Se si modifica l'estensione di un file e lo si carica, il tipo MIME è il MIME del contenuto effettivo del file, non l'estensione. Tuttavia, è probabile che non sia consentito accettare input come URL. Non ne sono sicuro.
mbomb007,

@ mbomb007 Non sono sicuro del perché lo dici; i tipi mime sono una cosa su Internet, non un filesystem / file, e i server di cui sono a conoscenza lo determineranno in base all'estensione usando una ricerca configurata (per la velocità di servire le intestazioni; non vogliono ispezionare tutti i file prima di servire esso). Prendiamo ad esempio Apache AddType <mime> <extension>o IIS <MimeMap>. Ovviamente un setup specifico o uno strumento di file hosting potrebbe fare un'ispezione adeguata, e ciò meriterebbe che la scelta del server faccia parte della risposta (dato che è il server che determina il tipo di file!)
Dave,

1
Ho eseguito la convalida dei file con .NET e il tipo MIME corrisponde al contenuto anche quando l'estensione è stata modificata prima del caricamento.
mbomb007,

@ mbomb007 quindi qualsiasi componente .NET che hai usato deve aver eseguito l'ispezione dei file durante il caricamento o durante la pubblicazione dei file (immagino che durante il caricamento per le prestazioni, ma non lo sai mai). Quindi, tornando al mio commento originale, questo risponderebbe a qualcosa come "JavaScript + .NET SeverLibraryXYZ". Per quanto riguarda l'accettazione di input da un URL, posso capire perché sei titubante, ma personalmente lo considererei valido fintanto che viene menzionata la scelta del server. Forse esiste una meta esistente, ma alla fine ovviamente dipende da Mego.
Dave,

1

Chip , 11 byte

~Z~S
t'G~aF

Replica spudoratamente replicata della gelatina di Dennis in Chip.

Ritorni senza 0x0perdita, ritorni con perdita 0x1.

Provalo online , link in sintesi (grazie Dennis per la strategia TIO qui)

Spiegare!

~Z~S
t'

Questa porzione è il servizio di pulizia: Staglia il primo byte e si trompe dopo il secondo.

G~aF

Questa è la carne della decisione. Ogni byte di input è accessibile dai bit HGFEDCBA. Se Gè impostato, e Fnon è, significa che il byte è all'interno della gamma 0x40a 0x5f(che è più o meno equivalente a 'maiuscolo', e abbastanza buono per il compito a portata di mano).

Tuttavia, per il risparmio di byte, inverto questa decisione da G and (not F)a (not G) or F, poiché o può essere implicito in Chip.

Questo risultante valore vero / falso viene quindi inserito in a, che è il bit più basso dell'output. (Tutti gli altri bit saranno zero). Nel TIO, eseguo l'output tramite hexdump in modo che i valori siano visibili.

Equivalentemente, in C-ish, si direbbe qualcosa del tipo:

out_byte = !(in_byte & 0x40) && (in_byte & 0x20)

1

Cubix, 16 byte

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

Modulo netto:

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

Provate voi stessi

È necessario immettere i valori dei byte decimali del file in un elenco separato. Il separatore non ha importanza, tutto ciò che non è una cifra o un segno meno è sufficiente. Il codice si preoccupa solo del primo byte, quindi puoi lasciare fuori il resto del file se vuoi. Il programma esce 0per lossless e 1per lossy. Provalo qui ! L'input predefinito utilizza un'intestazione FLAC.

Spiegazione

La cosa bella dei file è che (quasi) tutti hanno una cosiddetta magia. Questi sono i primi pochi byte del file. Un buon software non controlla l'estensione del file, ma piuttosto la magia dei file per vedere se è in grado di gestire un determinato file.

Dennis ha trovato il modo di usare questa magia per trovare il tipo di compressione, ma il fatto che abbia scartato il primo byte mi ha fatto venire voglia di provare a trovare un metodo che usasse il primo byte, piuttosto che il secondo. Dopotutto, questa community si occupa del salvataggio dei byte.

Ecco un elenco dei primi byte dei diversi tipi di file. Li ho ordinati in due gruppi: lossy e lossless. Ecco i valori del loro primo byte in decimale, esadecimale e binario. Potresti già vedere uno schema ...

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

Lo schema che ho visto era che il secondo bit (contato da sinistra a destra) era sempre attivo sui byte "lossless" e il quinto bit era sempre spento. Questa combinazione non appare in nessuno dei formati con perdita. Per "estrarre" questo, dovremmo semplicemente fare un binario AND (by 0b01001000 (=72)) e poi confrontarlo con 0b01000000 (=64). Se entrambi sono uguali, il formato di input è senza perdita, altrimenti è con perdita.

Sfortunatamente, Cubix non ha un simile operatore di confronto, quindi ho usato la sottrazione (se il risultato è 64, questo produce 0 e risulta in 8, -56 o -64 altrimenti. Tornerò su questo più tardi.

Innanzitutto, iniziamo all'inizio del programma. Il binario AND viene eseguito utilizzando il acomando:

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

Quindi, confrontiamo con 64 usando la sottrazione (nota che colpiamo un mirror che riflette l'IP sulla faccia superiore [prima riga, secondo carattere, che punta a sud] nel mezzo di questa parte).

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

Dopo che l'IP è invertito da u, usiamo un po 'di flusso di controllo per spingere 1a nello stack se (e solo se) la parte superiore dello stack è diversa da zero:

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

Dopo aver avvolto il cubo, abbiamo colpito l' <istruzione, che indica l'IP ovest sulla quarta riga. Tutto quello che resta da fare è produrre e terminare.

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

Quindi, il programma esce 0per lossless e 1per lossy.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.