Pull to refresh

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

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

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

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

Осталось два варианта: или попытаться когда-нибудь найти другую копию, которая будет успешно копироваться (маловероятно), или каким-то образом восстановить исходные звуковые данные c повреждённых дисков. Вы уже знаете, какой вариант я выбрал.

Как работает риппер



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

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

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

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

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

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

«Я миновал десять тысяч проходов, десять тысяч проходов, чтобы увидеть вас»



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

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

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

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

«Количество переходит в качество»


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


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

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


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

Кустарное творчество




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

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

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



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

Errors total Num : 206645159
Errors (Loudness) Num : 965075 - Avg : -21.7 dB(A) - Max : -5.5 dB(A)
Error Muting Num : 154153 - Avg : 99.1 Samples - Max : 3584 Samples
Skips Num : 103 - Avg : 417.3 Samples - Max : 2939 Samples

Total Test Result : 45.3 points (of 100.0 maximum)




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

Errors total Num : 2331952
Errors (Loudness) Num : 147286 - Avg : -77.2 dB(A) - Max : -13.2 dB(A)
Error Muting Num : 8468 - Avg : 1.5 Samples - Max : 273 Samples
Skips Num : 50 - Avg : 6.5 Samples - Max : 30 Samples

Total Test Result : 62.7 points (of 100.0 maximum)


«С определённого момента числа имеют значение»


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

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


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

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

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

0xA595BC09


Используя почти идеальную коррекцию ошибок от 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${RIP_ID}/*.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.

Приложение A: compare.rs


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

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. Подозреваю, что тот риппер не понял, что у него плохой диск. [вернуться]

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

Articles