Lossy или Lossless?


18

Для данного аудиофайла определите, закодирован ли он в формате с потерями или в формате без потерь. Для целей этой задачи необходимо классифицировать только следующие форматы:

правила

  • Если ввод принимается в форме имени файла, не следует делать никаких предположений относительно имени файла (например, расширение не гарантируется для формата правильно или даже не присутствует).
  • Во входных файлах не будет метаданных ID3 или APEv2.
  • Могут использоваться любые два уникальных и различимых выхода, таких как 0и 1, lossyи lossless, fooи bar, и т. Д.

Тестовые случаи

Контрольные примеры для этой задачи состоят из расположенного здесь zip-файла, который содержит две директории: lossyи lossless. Каждый каталог содержит несколько аудиофайлов, которые представляют собой синусоидальные волны 440 Гц по 0,5 секунды, закодированные в различных форматах. Все аудиофайлы имеют расширения, соответствующие указанным выше форматам, за исключением A440.m4a(это аудио AAC в контейнере MPEG Layer 4).


« Аудио AAC в контейнере MPEG Layer 4 » поднимает вопрос: какие другие форматы контейнеров должны обрабатывать ответы?
Питер Тейлор

@PeterTaylor Только AAC было уделено особое внимание, потому что я не мог найти способ предоставить аудио AAC без встраивания его в контейнер MPEG Layer 4 через FFMPEG. Аудио Vorbis встроено в контейнер Ogg (что является нормой для аудио Vorbis). Все остальные являются автономными форматами.
Mego

Вы уверены в файле TTA? Согласно спецификации , файлы TTA должны начинаться с магического номера TTA1 или TTA2. FFM2 (магический номер вашего файла) соответствует потоку FFmpeg. Файл Linux распознает заголовок TTA1, но не заголовок FFM2.
Деннис

Кроме того, можем ли мы предположить, что AAC всегда будет в заголовке MPEG Layer 4? Если нет, то может мы предполагаем?
Деннис

Можем ли мы взять содержимое файла в качестве входных данных или наш код должен их получить?
Лохматый

Ответы:


18

Желе , 7 5 байт

ƈƈeØA

Форматы с потерями возвращают 0 , форматы без потерь возвращают 1 .

Попробуйте онлайн! (постоянные ссылки в Gist)

Фон

Форматы, которые мы должны поддерживать, имеют следующие магические числа, т.е. они начинаются с этих байтов.

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

Записи с отступом - это контейнеры для предыдущего формата, которые появляются в тестовых примерах. ?обозначает переменный байт. .обозначает непечатаемый байт. Все остальные байты отображаются в виде символов ISO 8859-1.

Посмотрев только на второй байт, мы можем легко определить формат:

Форматы без потерь имеют заглавную букву в качестве второго байта, а форматы с потерями - нет.

Как это устроено

ƈƈ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
Это очень умное решение.
Mego

10

C 82 80 32 байта

Вдохновленный ответом @Dennis , это может быть значительно уменьшено:

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

Передайте данные файла в stdin. Возвращает 0 для потерь или ненулевое значение для потерь.

Или оригинал дольше проверяю:

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

Передайте данные файла в stdin. Возвращает ненулевое значение (1) для потерь или 0 для потерь.

Из того, что я могу сказать, все форматы, которые вы перечислили, имеют отдельные магические числа (кроме AIFF / WAV, но оба они в любом случае без потерь), так что это просто проверяет это магическое число на известное значение без потерь. Это *v&&просто для защиты от сопоставления файлов, которые начинаются с нулевого байта (M4A).

Я включил значения, которые я нашел в спецификациях ( fLaC= FLAC, RIFF= WAV / AIFF, TTA1= TTA), и FORM= AIFF и FFM2= TTA взяты из предоставленных примеров файлов (я могу только догадываться, что это форматы обертки или более поздние версии).


Или более короткая, похожая на обман, альтернатива:

Bash + файл, 61 байт

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

Принимает имя файла в качестве аргумента. Возвращает 0 для потерь или ненулевое значение для потерь.

Делает именно то, что вы ожидаете; спрашивает, fileчто это за тип файла, затем проверяет известные шаблоны. Совпадения TTA : d( : data), совпадения AIFF / WAV IFи совпадения FLAC FL. Ни один из результатов без потерь не соответствует ни одному из них, и я протестировал, что он все еще работает, если имена файлов удалены.


Тестирование:

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

Ваше решение Bash также работает, если расширение файла неверно? «расширение не обязательно будет правильным для формата», поэтому вы должны иметь возможность дать файлу неправильное расширение и все же заставить его работать.
mbomb007

@ mbomb007 Я только что проверил с перепутанными расширениями, и он все еще идентифицирует их хорошо. Я думаю, fileчто в любом случае не доверяет расширениям (для многих пользователей переименование png в jpeg - это то же самое, что преобразование!)
Дейв,

7

GS2 , 3 байта

◄5ì

Форматы с потерями возвращают 0 , форматы без потерь возвращают 1 .

Попробуйте онлайн! (постоянные ссылки в Gist)

Фон

Форматы, которые мы должны поддерживать, имеют следующие магические числа, т.е. они начинаются с этих байтов.

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

Записи с отступом - это контейнеры для предыдущего формата, которые появляются в тестовых примерах. ?обозначает переменный байт. .обозначает непечатаемый байт. Все остальные байты отображаются в виде символов ISO 8859-1.

Посмотрев только на второй байт, мы можем легко определить формат:

Форматы без потерь имеют заглавную букву в качестве второго байта, а форматы с потерями - нет.

Как это устроено

     (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 байт

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

объяснение

Принимает содержимое файла в качестве входных данных и возвращает , trueесли файл является без потерь или , falseесли он выполняется с потерей данных путем тестирования первого символа этого входа , чтобы увидеть , если это f, F, Rили T.


Попытайся

Вставьте содержимое файла в textarea.

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


Второе усилие, 81 63 байта

Извлекает содержимое файла с предоставленного URL, который оказался излишним.

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

Первое усилие, 146 116 89 байт

Неверно, так как типы MIME привязаны к расширениям, и, очевидно, заголовки ответа квалифицируются как дополнительный ввод.

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

веб-серверы обычно генерируют MIME на основе расширения файла, что здесь противоречит правилам. Вы проверили, работает ли он на файлах, обслуживаемых без расширения? (если это произойдет, то вам, вероятно, следует включить имя сервера, который вы используете, как часть «языка»)
Дейв,

1
@ Дэйв: Уверен, что нет. MIME и расширение вообще не зависят друг от друга. Если вы измените расширение файла и загрузите его, тип MIME - это MIME фактического содержимого файла, а не расширение. Тем не менее, принимать входные данные в качестве URL-адреса, вероятно, недопустимо. Я не уверен.
mbomb007

@ mbomb007 Я не уверен, почему ты так говоришь; MIME-типы - это Интернет, а не файловая система / файл, и серверы, о которых я знаю, будут определять его по расширению, используя настроенный поиск (для скорости обслуживания заголовков; они не хотят проверять каждый файл перед обслуживанием). Это). Взять, к примеру, Apache AddType <mime> <extension>или IIS <MimeMap>. Конечно, определенная установка или инструмент для размещения файлов могли бы провести надлежащую проверку, и это было бы целесообразно, чтобы выбор сервера был частью ответа (поскольку именно сервер определяет
Дейв

1
Я провел проверку файлов с помощью .NET, и тип MIME соответствовал содержимому, даже если расширение было изменено перед загрузкой.
mbomb007

@ mbomb007, то какой бы компонент .NET вы использовали, он должен был выполнить проверку файлов либо во время загрузки, либо при обслуживании файлов (я думаю, во время загрузки для производительности, но вы никогда не знаете). Итак, возвращаясь к моему первоначальному комментарию, этот ответ будет выглядеть примерно так: «JavaScript + .NET SeverLibraryXYZ». Что касается ввода от URL-адреса, я могу понять, почему вы не решаетесь, но лично я считаю его действительным, пока упоминается выбор сервера. Может быть, есть существующая мета, но в конечном счете, конечно, это зависит от Мего.
Дейв

1

Чип , 11 байт

~Z~S
t'G~aF

Бесстыдно воспроизведенный ответ Желе Денниса в Чипе.

Возвращение без потерь, возвращение 0x0с потерями 0x1.

Попробуйте онлайн , ссылки в гисте (спасибо Денису за стратегию TIO здесь)

Объясните!

~Z~S
t'

Эта часть является служебной: она Sубивает первый байт и tзавершается после второго.

G~aF

Это мясо решения. Каждый входной байт доступен по битам HGFEDCBA. Если Gэто множество, и Fнет, это означает , что байты находятся в пределах диапазона 0x40от 0x5f(что примерно эквивалентно «верхнего регистра», и достаточно хороши для выполнения этой задачи под руку).

Однако, для экономии байтов, я инвертирую это решение из G and (not F)в (not G) or F, так как или может быть неявным в Chip.

Это результирующее значение true / false затем помещается в a, который является младшим битом вывода. (Все остальные биты будут равны нулю). В TIO я запускаю вывод через hexdump, чтобы значения были видны.

Эквивалентно, в C-ish можно сказать что-то вроде:

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

1

Cubix, 16 байтов

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

Чистая форма:

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

Попробуй сам

Вы должны ввести десятичные байтовые значения файла в отдельный список. Разделитель не имеет значения, достаточно всего, что не является цифрой или знаком минус. Код действительно заботится только о первом байте, поэтому вы можете оставить остальную часть файла, если хотите. Программа выводит 0как без потерь, так и 1с потерями. Попробуй это здесь ! Вход по умолчанию использует заголовок FLAC.

объяснение

Хорошая вещь о файлах состоит в том, что (почти) все они имеют так называемую магию. Это первые несколько байтов файла. Хорошее программное обеспечение проверяет не расширение файла, а волшебство файла, чтобы увидеть, сможет ли оно обработать определенный файл.

Деннис нашел способ использовать эту магию, чтобы найти тип сжатия, но тот факт, что он отбросил первый байт, заставил меня захотеть придумать метод, который использовал первый байт, а не второй. В конце концов, это сообщество о сохранении байтов.

Вот список первых байтов разных типов файлов. Я разделил их на две группы: с потерями и без потерь. Вот значения их первого байта в десятичном, шестнадцатеричном и двоичном виде. Вы можете увидеть шаблон уже ...

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

Шаблон, который я увидел, состоял в том, что второй бит (считая слева направо) всегда был включен в байтах «без потерь», а пятый бит был всегда выключен. Эта комбинация не отображается ни в одном из форматов с потерями. Чтобы «извлечь» это, мы просто сделали бы двоичное И (by 0b01001000 (=72)) и затем сравнили бы с 0b01000000 (=64). Если оба равны, формат ввода без потерь, в противном случае это с потерями.

К сожалению, у Cubix нет такого оператора сравнения, поэтому я использовал вычитание (если результат равен 64, это дает 0, а в противном случае - 8, -56 или -64. Я вернусь к этому позже.

Сначала давайте начнем с начала программы. Двоичный И делается с помощью aкоманды:

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

Затем мы сравниваем с 64, используя вычитание (обратите внимание, что мы ударяем зеркало, которое отражает IP на верхнюю грань [первая строка, второй символ, указывающий на юг] в середине этой части).

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

После того, как IP обернут IP u, мы используем некоторый поток управления для перемещения a 1в стек, если (и только если) вершина стека отлична от нуля:

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

После обтекания куба мы нажимаем на <инструкцию, которая указывает IP на запад на четвертой строке. Все, что осталось сделать, это вывести и завершить.

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

Итак, программа выводит 0как без потерь, так и 1с потерями.

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.