Ключевые слова:cdrom, audio, sound, gcc, example, (найти похожие документы)
From: Bob <ubob@mail.ru.>
Newsgroups: email
Date: Mon, 28 Jan 2005 14:31:37 +0000 (UTC)
Subject: Написание программы для копирования треков с Audio-CD
Копирование треков с 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.