Копирование треков с Audio-CD

В статье рассматривается исходный текст программы для считывания
аудиоданных с музыкального компакт-диска (Audio-CD)

и сохранения их в WAV-файле. Программа функционирует под управлением ОС Linux.

Для понимания изложенного в статье материала необходимо знать структуру
и форматы данных, хранящихся на компакт-диске.

Эту информацию можно получить из следующих источников:

1. Information specification INF-8020i Rev 2.6. ATA Packet Interface for
   CD-ROMs SFF-8020i, http://www.stanford.edu/~csapuntz/specs/INF-8020.PDF

2. Introduction to CD and CD-ROM (with information on CD and CD-ROM
   formats, complete with diagrams and tables),
   http://www.disctronics.co.uk/downloads/tech_docs/cdintroduction.pdf

3. Крис Касперски. "Техника защиты компакт-дисков от копирования",
   издательство "BHV", 2004 г.

4. CD-Recordable FAQ, http://www.cdrfaq.org

5. Кулаков В. "Програмирование дисковых подсистем", издательство
   "Питер", 2002 г.

6. Comprising a comprehensive list of terms and words used in connection
   with CDs and DVDs and the applications that they support,
   http://www.disctronics.co.uk/downloads/tech_docs/glossary.pdf

 

Заголовочные файлы:

#include <stdio.h>
#include <errno.h>
#include <fcntl.h>
#include <linux/cdrom.h>
#include <linux/types.h>
#include <sys/stat.h>

#define CD_DEVICE "/dev/cdrom" // имя файла устройства
#define WAV_HDR_LEN 44 // размер WAV-заголовка
#define WAV_HDR_LEN 44 // размер WAV-заголовка

Следующая структура описывает формат WAV-заголовка (взято из исх.
текстов cdda2wav)

typedef struct {
    __u8 riff[4];
    __u32 size;
    __u8 wave[8];
    __u32 size1;
    __u16 format_tag;
    __u16 channels;
    __u32 sample_per_sec;
    __u32 byte_per_sec;
    __u16 block_align;
    __u16 bit_per_sample;
    __u8 data[4];
    __u32 size2;
} __attribute__ ((packed)) wav_header_t;

wav_header_t w_hdr;

int main()
{
    int fd, out, n;
    unsigned int i, start_lba, end_lba;
    unsigned int i, start_lba, end_lba;

Буфер для аудиоданных:

    __u8 buff[CD_FRAMESIZE_RAW];
    __u8 buff[CD_FRAMESIZE_RAW];

Значение CD_FRAMESIZE_RAW определено в файле <linux/cdrom.h>:

#define CD_FRAMESIZE_RAW 2352

    struct stat s;
    struct stat s;

Следующие структуры определены в файле <linux/cdrom.h>:

    struct cdrom_tochdr hdr;
    struct cdrom_tocentry toc;
    struct cdrom_read_audio cda;
    struct cdrom_read_audio cda;

Структура struct cdrom_tochdr содержит заголовок таблицы содержания
диска (Table of Contents, TOC) - номера первого и последнего треков (см.
спецификацию INF-8020i, стр.171, табл.127):

/* This struct is used by the CDROMREADTOCHDR ioctl */
struct cdrom_tochdr 
{
    __u8 cdth_trk0; /* start track */
    __u8 cdth_trk1; /* end track */
};
};

Структрура struct cdrom_tocentry содержит дескриптор трека (INF-8020i,
стр.171, табл.127):

/* This struct is used by the CDROMREADTOCENTRY ioctl */
struct cdrom_tocentry
{
    __u8 cdte_track;
    __u8 cdte_adr :4;
    __u8 cdte_ctrl :4;
    __u8 cdte_format;
    union cdrom_addr cdte_addr;
    __u8 cdte_datamode;
};
};

Поле cdte_track содержит номер трека, поле cdte_format определяет формат
адреса - MSF (Minute/Second/Frame) или LBA. Значения этого поля
определены в <linux/cdrom.h>:

/* CD-ROM address types (cdrom_tocentry.cdte_format) */
#define CDROM_LBA 0x01 /* "logical block": first frame is #0 */
#define CDROM_MSF 0x02 /* "minute-second-frame": binary, not bcd here! */
#define CDROM_MSF 0x02 /* "minute-second-frame": binary, not bcd here! */

В зависимости от выбранного формата адреса используется одно из полей
объединения union cdrom_addr:

/* Address in either MSF or logical format */
union cdrom_addr 
{
    struct cdrom_msf0 msf;
    int lba;
};
};

Структура struct cdrom_read_audio используется для хранения аудиоданных:

/* This struct is used by the CDROMREADAUDIO ioctl */
struct cdrom_read_audio
{
    union cdrom_addr addr; /* frame address */
    __u8 addr_format; /* CDROM_LBA or CDROM_MSF */
    int nframes; /* number of 2352-byte-frames to read at once */
    __u8 *buf; /* frame buffer (size: nframes*2352 bytes) */
};
};

Открываем файл устройства:

    fd = open(CD_DEVICE, O_RDONLY|O_NONBLOCK);
    if(fd < 0) {
        perror("open");
        return -1;
    }
    }

Проверяем тип компакт-диска. Это должен быть Audio-CD:

    if(ioctl(fd, CDROM_DISC_STATUS) != CDS_AUDIO) {
        printf("I need Audio-CD!\n");
        return 0;
    }
    }

Определяем число треков на компакт-диске:

    memset((void *)&hdr, 0, sizeof(struct cdrom_tochdr));
    if(ioctl(fd, CDROMREADTOCHDR, &hdr) < 0) {
        perror("ioctl");
        return(errno);
    }

    printf("First: %d\t", hdr.cdth_trk0);
    printf("Last: %d\n", hdr.cdth_trk1);

#define FIRST hdr.cdth_trk0
#define LAST hdr.cdth_trk1
#define LAST hdr.cdth_trk1

Вводим номер трека, который мы хотим считать с диска:

    printf("Enter track number: ");
    scanf("%d", &n);

    if((n < 1) || (n > LAST)) {
        printf("Wrong track number\n");
        return -1;
    }
    }

Задаем формат адреса LBA и считываем стартовые координаты трека:

    toc.cdte_format = CDROM_LBA;
    toc.cdte_track = n;

    if(ioctl(fd, CDROMREADTOCENTRY, &toc) < 0) {
        perror("ioctl");
        return -1;
    }

    start_lba = toc.cdte_addr.lba; // стартовый адрес трека
    start_lba = toc.cdte_addr.lba; // стартовый адрес трека

Конечный адрес трека определим как стартовый адрес следующего трека.
Если мы выбрали последний трек на диске, то необходимо определить начало
Lead-Out области диска

    if(n == LAST) toc.cdte_track = CDROM_LEADOUT;
    else toc.cdte_track = n + 1;

    if(ioctl(fd, CDROMREADTOCENTRY, &toc) < 0) {
        perror("ioctl");
        return -1;
    }

    end_lba = toc.cdte_addr.lba; // конечный адрес трека
    end_lba = toc.cdte_addr.lba; // конечный адрес трека

Создаем файл track.wav для хранения считанных аудиоданных:

    out = open("./track.wav", O_CREAT|O_RDWR|O_TRUNC, 0600);
    if(out < 0) {
        perror("open");
        return -1;
    }
    }

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

    memset(&w_hdr, 0, sizeof(wav_header_t));
    write(out, (void *)&w_hdr, WAV_HDR_LEN);
    write(out, (void *)&w_hdr, WAV_HDR_LEN);

Начинаем считывать аудиоданные. При каждом обращении к диску считываем
один фрейм (2352 байта), адресация в формате LBA, считанные данные
помещаем в буфер buff, а затем записываем в файл track.wav

    cda.addr_format = CDROM_LBA;
    cda.nframes = 1;
    cda.buf = buff;

    printf("Track size - %d sectors\n", end_lba - start_lba);

    for(i = start_lba; i < end_lba; i++) {

        memset(buff, 0, sizeof(buff));

        cda.addr.lba = i;
        printf("%c", 0x0D);
        printf("lba: %u", i - start_lba + 1);

        /* Читаем аудиоданные*/
        if(ioctl(fd, CDROMREADAUDIO, &cda) < 0) {
            perror("ioctl");
            return -1;
        }

        if(write(out, (__u8 *)buff, CD_FRAMESIZE_RAW) < 0) {
            perror("write");
            return -1;
        }
    }

    printf("\n");
    printf("\n");

Определяем размер файла track.wav:

    memset(&s, 0, sizeof(struct stat));
    if(fstat(out, &s) < 0) {
        perror("fstat");
        exit(-1);
    }
    }

Теперь необходимо сформировать WAV-заголовок и записать его в начало файла:

    memset(&w_hdr, 0, sizeof(wav_header_t));
    memcpy(w_hdr.riff, "RIFF", 4);
    w_hdr.size = s.st_size - 8;
    memcpy(w_hdr.wave, "WAVEfmt ", 8); // последний символ - пробел
    w_hdr.size1 = 16;
    w_hdr.format_tag = 1;
    w_hdr.channels = 2;
    w_hdr.sample_per_sec = 44100;
    w_hdr.byte_per_sec = 176400;
    w_hdr.block_align = 4;
    w_hdr.bit_per_sample = 16;
    memcpy(w_hdr.data, "data", 4);
    w_hdr.size2 = s.st_size - WAV_HDR_LEN;
    w_hdr.size2 = s.st_size - WAV_HDR_LEN;

Записываем сформированный заголовок в файл track.wav:

    lseek(out, 0, 0);
    write(out, (void *)&w_hdr, WAV_HDR_LEN);
    printf("OK\n");

    close(fd);
    close(out);

    return 0;
}
}

Полученный в результате работы программы файл track.wav можно
сконвертировать в любой цифровой формат - MP3 или Ogg Vorbis, например:

        # oggenc -b 192 track.wav

В результате получаем файл track.ogg, который можно прослушать при
помощи утилиты ogg123:

        # ogg123 track.ogg

Утилиты и библтотеки для работы с файлами формата Ogg Vorbis можно
скачать с сайта http://www.vorbis.com.

Добавить комментарий

Обратная связь

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

пишите мне на netdm@mail.ru