Implemented reading from CD and prepending a fake WAV header - not yet tested, though.

git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@21151 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
Axel Dörfler 2007-05-16 09:19:42 +00:00
parent 12ed6d2195
commit 90c18b97c9
4 changed files with 194 additions and 3 deletions

View File

@ -10,6 +10,7 @@
#include <device/scsi.h>
#include <ctype.h>
#include <errno.h>
#include <stdlib.h>
#include <string.h>
@ -411,6 +412,46 @@ dump_toc(scsi_toc_toc *toc)
}
static status_t
read_frames(int fd, off_t frame, uint8 *buffer, size_t count)
{
size_t framesLeft = count;
while (framesLeft > 0) {
scsi_read_cd read;
read.start_m = frame / kFramesPerMinute;
read.start_s = (frame / kFramesPerSecond) % 60;
read.start_f = frame % kFramesPerSecond;
read.length_m = count / kFramesPerMinute;
read.length_s = (count / kFramesPerSecond) % 60;
read.length_f = count % kFramesPerSecond;
read.buffer_length = count * kFrameSize;
read.buffer = (char *)buffer;
read.play = false;
if (ioctl(fd, B_SCSI_READ_CD, &read) < 0) {
// drive couldn't read data - try again to read with a smaller block size
if (count == 1)
return errno;
if (count >= 32)
count = 8;
else
count = 1;
continue;
}
buffer += count * kFrameSize;
framesLeft -= count;
frame += count;
}
return B_OK;
}
static status_t
read_table_of_contents(int fd, uint32 track, uint8 format, uint8 *buffer,
size_t bufferSize)
@ -559,3 +600,35 @@ read_table_of_contents(int fd, scsi_toc_toc *toc, size_t length)
return B_OK;
}
status_t
read_cdda_data(int fd, off_t offset, void *data, size_t length,
off_t bufferOffset, void *buffer, size_t bufferSize)
{
if (bufferOffset >= 0 && bufferOffset <= offset + length
&& bufferOffset + bufferSize > offset) {
// TODO: fill request from buffer
}
while (length > 0) {
off_t frame = offset / kFrameSize;
uint32 count = bufferSize / kFrameSize;
status_t status = read_frames(fd, frame, (uint8 *)buffer, count);
if (status < B_OK)
return status;
off_t dataOffset = offset - frame * kFrameSize;
size_t bytes = bufferSize - dataOffset;
if (bytes > length)
bytes = length;
if (user_memcpy(data, (uint8 *)buffer + dataOffset, bytes) < B_OK)
return B_BAD_ADDRESS;
length -= bytes;
offset += bytes;
}
return B_OK;
}

View File

@ -30,5 +30,7 @@ struct cdtext {
status_t read_cdtext(int fd, cdtext &text);
status_t read_table_of_contents(int fd, scsi_toc_toc *toc, size_t length);
status_t read_cdda_data(int fd, off_t offset, void *data, size_t length,
off_t bufferOffset, void *buffer, size_t bufferSize);
#endif // CDDA_H

View File

@ -40,6 +40,32 @@ struct dir_cookie;
typedef DoublyLinkedList<Attribute> AttributeList;
typedef DoublyLinkedList<attr_cookie> AttrCookieList;
struct riff_header {
uint32 magic;
uint32 length;
uint32 id;
} _PACKED;
struct riff_chunk {
uint32 fourcc;
uint32 length;
} _PACEKD;
struct wav_format_chunk : riff_chunk {
uint16 format_tag;
uint16 channels;
uint32 samples_per_second;
uint32 average_bytes_per_second;
uint16 block_align;
uint16 bits_per_sample;
} _PACKED;
struct wav_header {
riff_header header;
wav_format_chunk format;
riff_chunk data;
} _PACKED;
class Volume {
public:
Volume(mount_id id);
@ -62,7 +88,10 @@ class Volume {
Inode *Find(const char *name);
Inode *FirstEntry() const { return fFirstEntry; }
off_t NumBlocks() const { return fNumBlocks; }
size_t BufferSize() const { return 32 * kFrameSize; }
// TODO: for now
static void DetermineName(cdtext &text, char *name, size_t length);
@ -147,6 +176,8 @@ class Inode {
void RemoveAttrCookie(attr_cookie *cookie);
void RewindAttrCookie(attr_cookie *cookie);
const wav_header *WAVHeader() const { return &fWAVHeader; }
Inode *Next() const { return fNext; }
void SetNext(Inode *inode) { fNext = inode; }
@ -163,6 +194,7 @@ class Inode {
off_t fFrameCount;
AttributeList fAttributes;
AttrCookieList fAttrCookies;
wav_header fWAVHeader;
};
struct dir_cookie {
@ -184,6 +216,8 @@ struct attr_cookie : DoublyLinkedListLinkImpl<attr_cookie> {
struct file_cookie {
int open_mode;
off_t buffer_offset;
void *buffer;
};
@ -268,6 +302,7 @@ Volume::Mount(const char* device)
dprintf("CDDA: no CD-Text found.\n");
int32 trackCount = toc->last_track + 1 - toc->first_track;
off_t totalFrames = 0;
char title[256];
for (int32 i = 0; i < trackCount; i++) {
@ -302,6 +337,8 @@ Volume::Mount(const char* device)
title[j] = '-';
}
totalFrames += frames;
Inode *inode = _CreateNode(fRootNode, title, startFrame, frames,
S_IFREG | 0444);
if (inode == NULL)
@ -330,6 +367,7 @@ Volume::Mount(const char* device)
if (fName == NULL)
return B_NO_MEMORY;
fNumBlocks = totalFrames;
return B_OK;
}
@ -539,6 +577,32 @@ Inode::Inode(Volume *volume, Inode *parent, const char *name, off_t start,
fGroupID = parent ? parent->GroupID() : getegid();
fCreationTime = fModificationTime = time(NULL);
if (frames) {
// initialize WAV header
// RIFF header
fWAVHeader.header.magic = B_HOST_TO_LENDIAN_INT32('RIFF');
fWAVHeader.header.length = B_HOST_TO_LENDIAN_INT32(Size()
+ sizeof(wav_header) - sizeof(riff_chunk));
fWAVHeader.header.id = B_HOST_TO_LENDIAN_INT32('WAVE');
// 'fmt ' format chunk
fWAVHeader.format.fourcc = B_HOST_TO_LENDIAN_INT32('fmt ');
fWAVHeader.format.length = B_HOST_TO_LENDIAN_INT32(
sizeof(wav_format_chunk) - sizeof(riff_chunk));
fWAVHeader.format.format_tag = B_HOST_TO_LENDIAN_INT16(1);
fWAVHeader.format.channels = B_HOST_TO_LENDIAN_INT16(2);
fWAVHeader.format.samples_per_second = B_HOST_TO_LENDIAN_INT32(44100);
fWAVHeader.format.average_bytes_per_second = B_HOST_TO_LENDIAN_INT32(
44100 * sizeof(uint16) * 2);
fWAVHeader.format.block_align = B_HOST_TO_LENDIAN_INT16(4);
fWAVHeader.format.bits_per_sample = B_HOST_TO_LENDIAN_INT16(16);
// 'data' chunk
fWAVHeader.data.fourcc = B_HOST_TO_LENDIAN_INT32('data');
fWAVHeader.data.length = B_HOST_TO_LENDIAN_INT32(Size());
}
}
@ -943,6 +1007,7 @@ cdda_open(fs_volume _volume, fs_vnode _node, int openMode, fs_cookie *_cookie)
TRACE((" open cookie = %p\n", cookie));
cookie->open_mode = openMode;
cookie->buffer = NULL;
*_cookie = (void *)cookie;
@ -981,6 +1046,7 @@ cdda_read(fs_volume _volume, fs_vnode _node, fs_cookie _cookie, off_t offset,
void *buffer, size_t *_length)
{
file_cookie *cookie = (file_cookie *)_cookie;
Volume *volume = (Volume *)_volume;
Inode *inode = (Inode *)_node;
TRACE(("cdda_read(vnode = %p, offset %Ld, length = %lu, mode = %d)\n",
@ -990,10 +1056,52 @@ cdda_read(fs_volume _volume, fs_vnode _node, fs_cookie _cookie, off_t offset,
return B_IS_A_DIRECTORY;
if ((cookie->open_mode & O_RWMASK) != O_RDONLY)
return B_NOT_ALLOWED;
if (offset < 0)
return B_BAD_VALUE;
// TODO: read!
*_length = 0;
return B_OK;
off_t maxSize = inode->Size() + sizeof(wav_header);
if (offset >= maxSize) {
*_length = 0;
return B_OK;
}
if (cookie->buffer == NULL) {
// TODO: move that to open() to make sure reading can't fail for this reason?
cookie->buffer = malloc(volume->BufferSize());
if (cookie->buffer == NULL)
return B_NO_MEMORY;
cookie->buffer_offset = -1;
}
size_t length = *_length;
if (offset + length > maxSize)
length = maxSize - offset;
status_t status = B_OK;
if (offset < sizeof(wav_header)) {
// read fake WAV header
size_t size = sizeof(wav_header) - offset;
size = min_c(size, length);
if (user_memcpy(buffer, (uint8 *)inode->WAVHeader() + offset, size) < B_OK)
return B_BAD_ADDRESS;
length -= size;
}
if (length > 0) {
// read actual CD data
offset -= sizeof(wav_header);
status = read_cdda_data(volume->Device(), offset, buffer, length,
cookie->buffer_offset, cookie->buffer, volume->BufferSize());
}
if (status == B_OK)
*_length = length;
return status;
}

View File

@ -16,6 +16,14 @@
extern const char* __progname;
extern "C" status_t
user_memcpy(void *dest, const void *source, size_t length)
{
memcpy(dest, source, length);
return B_OK;
}
extern "C" void
dprintf(const char* format, ...)
{