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:
@@ -10,6 +10,7 @@
|
|||||||
#include <device/scsi.h>
|
#include <device/scsi.h>
|
||||||
|
|
||||||
#include <ctype.h>
|
#include <ctype.h>
|
||||||
|
#include <errno.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <string.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
|
static status_t
|
||||||
read_table_of_contents(int fd, uint32 track, uint8 format, uint8 *buffer,
|
read_table_of_contents(int fd, uint32 track, uint8 format, uint8 *buffer,
|
||||||
size_t bufferSize)
|
size_t bufferSize)
|
||||||
@@ -559,3 +600,35 @@ read_table_of_contents(int fd, scsi_toc_toc *toc, size_t length)
|
|||||||
return B_OK;
|
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;
|
||||||
|
}
|
||||||
|
|||||||
@@ -30,5 +30,7 @@ struct cdtext {
|
|||||||
|
|
||||||
status_t read_cdtext(int fd, cdtext &text);
|
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_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
|
#endif // CDDA_H
|
||||||
|
|||||||
@@ -40,6 +40,32 @@ struct dir_cookie;
|
|||||||
typedef DoublyLinkedList<Attribute> AttributeList;
|
typedef DoublyLinkedList<Attribute> AttributeList;
|
||||||
typedef DoublyLinkedList<attr_cookie> AttrCookieList;
|
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 {
|
class Volume {
|
||||||
public:
|
public:
|
||||||
Volume(mount_id id);
|
Volume(mount_id id);
|
||||||
@@ -62,7 +88,10 @@ class Volume {
|
|||||||
Inode *Find(const char *name);
|
Inode *Find(const char *name);
|
||||||
|
|
||||||
Inode *FirstEntry() const { return fFirstEntry; }
|
Inode *FirstEntry() const { return fFirstEntry; }
|
||||||
|
|
||||||
off_t NumBlocks() const { return fNumBlocks; }
|
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);
|
static void DetermineName(cdtext &text, char *name, size_t length);
|
||||||
|
|
||||||
@@ -147,6 +176,8 @@ class Inode {
|
|||||||
void RemoveAttrCookie(attr_cookie *cookie);
|
void RemoveAttrCookie(attr_cookie *cookie);
|
||||||
void RewindAttrCookie(attr_cookie *cookie);
|
void RewindAttrCookie(attr_cookie *cookie);
|
||||||
|
|
||||||
|
const wav_header *WAVHeader() const { return &fWAVHeader; }
|
||||||
|
|
||||||
Inode *Next() const { return fNext; }
|
Inode *Next() const { return fNext; }
|
||||||
void SetNext(Inode *inode) { fNext = inode; }
|
void SetNext(Inode *inode) { fNext = inode; }
|
||||||
|
|
||||||
@@ -163,6 +194,7 @@ class Inode {
|
|||||||
off_t fFrameCount;
|
off_t fFrameCount;
|
||||||
AttributeList fAttributes;
|
AttributeList fAttributes;
|
||||||
AttrCookieList fAttrCookies;
|
AttrCookieList fAttrCookies;
|
||||||
|
wav_header fWAVHeader;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct dir_cookie {
|
struct dir_cookie {
|
||||||
@@ -184,6 +216,8 @@ struct attr_cookie : DoublyLinkedListLinkImpl<attr_cookie> {
|
|||||||
|
|
||||||
struct file_cookie {
|
struct file_cookie {
|
||||||
int open_mode;
|
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");
|
dprintf("CDDA: no CD-Text found.\n");
|
||||||
|
|
||||||
int32 trackCount = toc->last_track + 1 - toc->first_track;
|
int32 trackCount = toc->last_track + 1 - toc->first_track;
|
||||||
|
off_t totalFrames = 0;
|
||||||
char title[256];
|
char title[256];
|
||||||
|
|
||||||
for (int32 i = 0; i < trackCount; i++) {
|
for (int32 i = 0; i < trackCount; i++) {
|
||||||
@@ -302,6 +337,8 @@ Volume::Mount(const char* device)
|
|||||||
title[j] = '-';
|
title[j] = '-';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
totalFrames += frames;
|
||||||
|
|
||||||
Inode *inode = _CreateNode(fRootNode, title, startFrame, frames,
|
Inode *inode = _CreateNode(fRootNode, title, startFrame, frames,
|
||||||
S_IFREG | 0444);
|
S_IFREG | 0444);
|
||||||
if (inode == NULL)
|
if (inode == NULL)
|
||||||
@@ -330,6 +367,7 @@ Volume::Mount(const char* device)
|
|||||||
if (fName == NULL)
|
if (fName == NULL)
|
||||||
return B_NO_MEMORY;
|
return B_NO_MEMORY;
|
||||||
|
|
||||||
|
fNumBlocks = totalFrames;
|
||||||
return B_OK;
|
return B_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -539,6 +577,32 @@ Inode::Inode(Volume *volume, Inode *parent, const char *name, off_t start,
|
|||||||
fGroupID = parent ? parent->GroupID() : getegid();
|
fGroupID = parent ? parent->GroupID() : getegid();
|
||||||
|
|
||||||
fCreationTime = fModificationTime = time(NULL);
|
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));
|
TRACE((" open cookie = %p\n", cookie));
|
||||||
cookie->open_mode = openMode;
|
cookie->open_mode = openMode;
|
||||||
|
cookie->buffer = NULL;
|
||||||
|
|
||||||
*_cookie = (void *)cookie;
|
*_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)
|
void *buffer, size_t *_length)
|
||||||
{
|
{
|
||||||
file_cookie *cookie = (file_cookie *)_cookie;
|
file_cookie *cookie = (file_cookie *)_cookie;
|
||||||
|
Volume *volume = (Volume *)_volume;
|
||||||
Inode *inode = (Inode *)_node;
|
Inode *inode = (Inode *)_node;
|
||||||
|
|
||||||
TRACE(("cdda_read(vnode = %p, offset %Ld, length = %lu, mode = %d)\n",
|
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;
|
return B_IS_A_DIRECTORY;
|
||||||
if ((cookie->open_mode & O_RWMASK) != O_RDONLY)
|
if ((cookie->open_mode & O_RWMASK) != O_RDONLY)
|
||||||
return B_NOT_ALLOWED;
|
return B_NOT_ALLOWED;
|
||||||
|
if (offset < 0)
|
||||||
|
return B_BAD_VALUE;
|
||||||
|
|
||||||
// TODO: read!
|
off_t maxSize = inode->Size() + sizeof(wav_header);
|
||||||
|
if (offset >= maxSize) {
|
||||||
*_length = 0;
|
*_length = 0;
|
||||||
return B_OK;
|
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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -16,6 +16,14 @@
|
|||||||
extern const char* __progname;
|
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
|
extern "C" void
|
||||||
dprintf(const char* format, ...)
|
dprintf(const char* format, ...)
|
||||||
{
|
{
|
||||||
|
|||||||
Reference in New Issue
Block a user