Главная » Хабрахабр » [Перевод] Зачем я рипнул один компакт-диск 300 раз

[Перевод] Зачем я рипнул один компакт-диск 300 раз

Я коллекционирую музыку: покупаю компакт-диски, оцифровываю их программой Exact Audio Copy, сканирую обложки и вкладыши. Иногда это непросто, если CD издан ограниченным тиражом за рубежом 10 лет назад. Сложнее всего, если на компакте производственный дефект — и некоторые треки не читаются.

Я нашёл его спустя три года (вероятно, на YouTube), скачал лучшую копию — и внёс диск в список будущих покупок. Альбом аранжировок для фортепиано 帰るべき城 от Altneuland вышел в 2005 году. К сожалению, ни один из моих CD-приводов не смог прочитать трек № 3. Последние достижения в технологиях международной почты позволили в прошлом году купить бэушный диск. Я отложил его и начал искать другой экземпляр, который нашёл в прошлом месяце. Такое часто бывает при покупке старых дисков, особенно когда они прошли через центр международной доставки USPS. Но с толкнулся с точно такой же ошибкой. Он прибыл в пятницу — и я сразу же попытался его рипнуть. Похоже, тут дело не в износе или повреждении — вероятно, диск вышел дефективным прямо с завода.

Когда я записываю начало или конец сбойной дорожки на пустой CD-R и копирую его, то риппер выдаёт ту же ошибку! ДОПОЛНЕНИЕ: После проведённого расследования я больше не считаю, что это заводской дефект. Вы уже знаете, какой вариант я выбрал. Попробуйте сами с файлом minimal.flac.
Осталось два варианта: или попытаться когда-нибудь найти другую копию, которая будет успешно копироваться (маловероятно), или каким-то образом восстановить исходные звуковые данные c повреждённых дисков.


EAC не смог прочитать трек № 3 с диска [帰るべき城]

Ошибки чтения возникают по разным причинам: грязный носитель, царапины на защитном слое поликарбоната, вибрации самого привода. CD хранят цифровые данные, но между дисками, лазерами и оптическими диодами вполне аналоговый интерфейс. Современные рипперы решают проблему с помощью двух важных методов обнаружения ошибок: избыточного чтения и AccurateRip. Примитивные коды коррекции ошибок в стандарте CDDA помогают минимизировать звуковые искажения на редко используемых дисках, но не способны полностью восстановить битовый поток на CD с большим количеством ошибок.

На странице EAC: Extraction Technology описано, как EAC производит избыточное чтение:

Такая процедура проводится максимум один, три или пять раз (в соответствии с выбранным качеством восстановления ошибок). В безопасном режиме программа считывает каждый сектор минимум дважды [...] Если возникает ошибка (чтения или синхронизации), то программа продолжает считывать этот сектор до тех пор, пока 8 из 16 попыток не окажутся идентичными. Так что в самом худшем случае плохие сектора считываются 82 раза!

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

EAC не даёт результат, если каждое чтение возвращает разные данные, а в базе AccurateRip только одна запись о редком диске [1]. Эта статья о том, что делать, если оба метода не могут помочь.


Оптические приводы Asus, LG, Lite-On, Pioneer и неизвестного OEM

Иногда конкретная модель более снисходительно относится к спецификациям CDDA или там лучшая прошивка для исправления ошибок, или что-то ещё. Если CD не копируется, то логично использовать другой привод. На форуме DBpoweramp есть рейтинг точности приводов CD/DVD, чтобы выбрать наиболее подходящий привод для рипа.

К сожалению, подтверждение рипа не удалось получить — между всеми рипами выходило около 20 000 отличающихся байт. В субботу утром я купил пять новых CD-приводов разных производителей [2], попробовал их все — и нашёл тот, который смог держать синхронизацию на битом треке.

Я рассудил, что ошибки чтения на плохом треке находятся где-то около «правильного». Но теперь у меня на диске были файлы .wav, а из этого можно извлечь пользу. Такой подход в итоге оказался успешным, но потребовал гораздо больше работы, чем я ожидал. Поэтому есть смысл сделать несколько рипов и найти «консенсусное» значение для нестабильных байтов.

Я начал с многократного копирования диска на одном из приводов, записи всех значений для каждого байта и объявления ошибки «исправимой», если более половины рипов выдаёт определённое байтовое значение для данной позиции. Начало было хорошим: количество неисправимых ошибок уменьшилось почти с ~6900 байт при N=4 до ~5000 байт при N=10. Выгода от каждого дополнительного рипа снижалась с течением времени, пока примерно на N=80 число неисправимых ошибок не стабилизировалось на уровне ~3700. Я прекратил рипы при N=100.


Исправленные и неисправимые ошибки на количество rip

Но не получилось: на каждом приводе оказались тысячи исправлений, которые не соответствовали исправлениям на другом! Затем я попытался 100 раз скопировать диск на втором приводе и использовать две карты коррекции, чтобы «заполнить» неисправимые позиции ошибок с первого привода. Оказывается, нельзя устранить шум, совместив его с другим, но связанным источником шума.


То же самое, но для двух дисков с перекрёстной проверкой исправлений

Это более низкоуровневая обработка ошибок, когда привод исправляет ошибки чтения, а не просто сообщает о них. На сайте EAC есть ещё один хороший ресурс: тест качества DAE, который определяет качество прошивки привода по уровню исправляемых ошибок. Загвоздка в том, что «безопасный режим» EAC доступен только при отключении этого встроенного кода коррекции ошибок, предполагая его неправильную работу.

Вот это — гарантированные неустранимые ошибки по детерминированному шаблоне. Я подготовил тест путём прожига файла .wav на CD-R, выделив точный сектор на поверхности данных и осторожно закрасив его чёрным маркером.

Я протестировал всех приводы и получил два интересных результата:

Он с удовольствием прожевал волшебный маркер, но его сильно смутили прямые линии на поверхности данных. Привод Lite-On я прежде использовал, чтобы обойти ошибки синхронизации. Вы можете видеть, как вместо трёх раздельных пиков справа один гигантский сбойный блоб.

7 dB(A) - Max : -5. Errors total Num : 206645159
Errors (Loudness) Num : 965075 - Avg : -21. 1 Samples - Max : 3584 Samples
Skips Num : 103 - Avg : 417. 5 dB(A)
Error Muting Num : 154153 - Avg : 99. 3 Samples - Max : 2939 Samples

3 points (of 100. Total Test Result : 45. 0 maximum)

На мой взгляд, график не выглядит каким-то особенным, но инструмент анализа сказал, что это лучшая прошивка для исправления ошибок в моём маленьком наборе. Привод Pioneer получил самый высокий балл по тесту DAE.

2 dB(A) - Max : -13. Errors total Num : 2331952
Errors (Loudness) Num : 147286 - Avg : -77. 5 Samples - Max : 273 Samples
Skips Num : 50 - Avg : 6. 2 dB(A)
Error Muting Num : 8468 - Avg : 1. 5 Samples - Max : 30 Samples

7 points (of 100. Total Test Result : 62. 0 maximum)

Как использовать прошивку Pioneer с хорошим исправлением ошибок, если «безопасный режим» EAC игнорирует её? Очень просто: переключите EAC в «пакетный режим» (burst mode) и записывайте на диск поток битов в том виде, в каком их сообщает прошивка. Как потом превратить эту кучу непроверенных файлов .wav в файл хорошего качества, как в «безопасном режиме»? Да тем же инструментом анализа ошибок, который мы использовали в рипах с Lite-On!

После нескольких настроек конфигурации EAC и через сто рипов мы получаем такую красивую диаграмму.


Исправленные и неисправимые ошибки на количество рипов (Pioneer)

Что можно отметить:

  • Неисправимые битовые ошибки быстро стремятся к нулю, но никогда его не достигают.
  • Огромный скачок исправленных ошибок в 53−54 рипах.
  • Количество ошибок до и после этого большого скачка практически не изменяется, что указывает на области стабильности в скопированных данных.

Используя почти идеальную коррекцию ошибок от Pioneer, я сгенерировал файл «лучшее предположение» и начал сравнивать его с рипами Pioneer. Как и ожидалось, обнаружилось несколько некачественных участков, которые я исправил, сделав ещё 10 рипов:

$ for RIP_ID in $(seq -w 1 100); do echo -n "rip$RIP_ID: "; cmp -l analysis-out.wav rips-cd1-pioneer/rip$/*.wav | wc -l ; done | sort -rgk2 | head -n 10
rip054: 2865
rip099: 974
rip007: 533
rip037: 452
rip042: 438
rip035: 404
rip006: 392
rip059: 381
rip043: 327
rip014: 323

Помните, ведь это как раз критерий успеха в «безопасном режиме» EAC. Я также нашёл что-то действительно интересное: несколько рипов выдавали абсолютно одинаковый контент! Команда shncat -q -e | rhash --print="%C" используется для вычисления контрольной суммы CRC32 необработанных аудиоданных: именно её применяет EAC.

$ for wav in rips-cd1-pioneer/*/*.wav; do shncat "$wav" -q -e | rhash --printf="%C $wav\n" - ; done | sort -k1
[...]9DD05FFF rips-cd1-pioneer/rip059/rip.wav
9F8D1B53 rips-cd1-pioneer/rip072/rip.wav
A2EA0283 rips-cd1-pioneer/rip082/rip.wav
A595BC09 rips-cd1-pioneer/rip021/rip.wav
A595BC09 rips-cd1-pioneer/rip022/rip.wav
A595BC09 rips-cd1-pioneer/rip023/rip.wav
A595BC09 rips-cd1-pioneer/rip024/rip.wav
A595BC09 rips-cd1-pioneer/rip025/rip.wav
A595BC09 rips-cd1-pioneer/rip026/rip.wav
A595BC09 rips-cd1-pioneer/rip027/rip.wav
A595BC09 rips-cd1-pioneer/rip028/rip.wav
A595BC09 rips-cd1-pioneer/rip030/rip.wav
A595BC09 rips-cd1-pioneer/rip031/rip.wav
A595BC09 rips-cd1-pioneer/rip040/rip.wav
A595BC09 rips-cd1-pioneer/rip055/rip.wav
A595BC09 rips-cd1-pioneer/rip058/rip.wav
AA3B5929 rips-cd1-pioneer/rip043/rip.wav
ABAAE784 rips-cd1-pioneer/rip033/rip.wav
[...]

И когда я проверил этот файл, там был точно такой же аудиоконтент, как и в «обычном» рипе! Тем временем повторные рипы некачественных участков позволили завершить анализ с нулём неисправимых ошибок. Этого достаточно, чтобы объявить победу.

Я на 99% уверен, что успешно скопировал этот проблемный компакт-диск, а 0xA595BC09 является правильной CRC-суммой для трека № 3.

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

extern crate memmap; use std::cmp;
use std::collections::HashMap;
use std::env;
use std::fs;
use std::sync;
use std::sync::mpsc;
use std::thread; use memmap::Mmap; const CHUNK_SIZE: usize = 1 << 20; fn suspect_positions( mmaps: &HashMap<String, Mmap>, start_idx: usize, end_idx: usize,
) -> Vec<usize> { let mut positions = Vec::new(); for ii in start_idx..end_idx { let mut first = true; let mut byte: u8 = 0; for (_file_name, file_content) in mmaps { if first { byte = file_content[ii]; first = false; } else if byte != file_content[ii] { positions.push(ii); break; } } } positions
} fn main() { let mut args: Vec<String> = env::args().collect(); args.remove(0); let mut first = true; let mut size: usize = 0; let mut files: Vec<fs::File> = Vec::new(); let mut mmaps: HashMap<String, Mmap> = HashMap::new(); for filename in args { let mut file = fs::File::open(&filename).unwrap(); files.push(file); let mmap = unsafe { Mmap::map(files.last().unwrap()).unwrap() }; if first { first = false; size = mmap.len(); } else { assert!(size == mmap.len()); } mmaps.insert(filename, mmap); } let (suspects_tx, suspects_rx) = mpsc::channel(); let mut start_idx = 0; let mmaps_ref = sync::Arc::new(mmaps); loop { let t_start_idx = start_idx; let t_end_idx = cmp::min(start_idx + CHUNK_SIZE, size); if start_idx == t_end_idx { break; } let mmaps_ref = mmaps_ref.clone(); let suspects_tx = suspects_tx.clone(); thread::spawn(move || { let suspects = suspect_positions(mmaps_ref.as_ref(), t_start_idx, t_end_idx); suspects_tx.send(suspects).unwrap(); }); start_idx = t_end_idx; } drop(suspects_tx); let mut suspects: Vec<usize> = Vec::with_capacity(size); for mut suspects_chunk in suspects_rx { suspects.append(&mut suspects_chunk); } suspects.sort(); println!("{{\"files\": ["); let mut first_file = true; for (file_name, file_content) in mmaps_ref.iter() { let file_comma = if first_file { "" } else { "," }; first_file = false; println!("{}{{\"name\": \"{}\", \"suspect_bytes\": [", file_comma, file_name); for (ii, position) in suspects.iter().enumerate() { let comma = if ii == suspects.len() - 1 { "" } else { "," }; println!("[{}, {}]{}", position, file_content[*position], comma); } println!("]}}"); } println!("]}}");
}

1. В этой единственной записи AccurateRip к моему диску совпадают CRC для всех треков, кроме трека № 3: там указана сумма 0x84B9DD1A, а у меня 0xA595BC09. Подозреваю, что тот риппер не понял, что у него плохой диск. [вернуться]

Очевидный вопрос при покупке CD- или DVD-привода в 2018 году: «Блин, а где ж их купить?». 2. Я знаю только один магазин поблизости, у которых в наличии есть DVD-приводы 5,25". И мне нужен был не один, а несколько от разных брендов. Конечно, я говорю о Frys Electronics. Только один магазин достаточно большой, чтобы не пожалеть место на полках на такие приводы, и достаточно странный, чтобы они не казались там неуместными. [вернуться]


Оставить комментарий

Ваш email нигде не будет показан
Обязательные для заполнения поля помечены *

*

x

Ещё Hi-Tech Интересное!

Написание собственной работоспособной ОС за пол года

Предыстория Здравствуйте! Всех категорически приветствую, сегодня хотел бы рассказать Вам о своём опыте написание работоспособной ОС под архитектуру x86.Как-то весенней ночью у меня родилась гениальная идея — попробовать себя в написании собственной ОС, которая может позволить запускать программы, работать с ...

Разработка классов-дескрипторов на C++/CLI

Шаблон Basic Dispose в C++/CLI        1.     Введение    1. Определение деструктора и финализатора        1. 1. Использование семантики стека    2. 2. 1. Управляемые шаблоны        2. 2. Интеллектуальные указатели        2. 3. Пример использования        2. Блокировка финализаторов    Список литературы NET Framework — редко используется для разработки больших самостоятельных проектов. C++/CLI — один ...