This is cddarec v0.20, a harddisk recorder for cdda files.

The usage is:  cddarec [options] [file].



0 Hints on producing non-clicking cdda files
=============================================

As stressed by Hannu Savolainen in his "Hacker's Guide to VoxWare 2.4",
recording 44.1kHz/16bit/stereo soundfiles (like cdda) on a multitasking
plattform like Linux is quite tricky. In contrast to manipulations of
a soundfile, recording (and playback) has to be performed in realtime.
Thus, the problem is the timing.

Before I talk on the history of this program, here some hints contributing
towards producing non-clicking cdda files:

- Keep the system load (especially disk access) as low as possible
- Switch your ide drive to (U)DMA mode which is possible at least with
  the 2.2.* kernels
- Play with the buffer size (option -s)
- Play with the number of buffers (option -n) (will probably not help much)
- if all fails: Try a single buffer (-n 1). This way the samples are put
  to RAM and are written only when the buffer is totally filled. Even if
  your system swaps (doesn't disturb recording on my (non-UDMA) machine),
  this is the last resort and should work anyway. However, the recording time
  is limited by your swap space (128 MB means 12 minutes and 40 seconds).



1 History of cddaread
=====================

As it might be expected, the first try has been a simple loop:

do {
  read( dsp_fd, buffer, size );
  write( out_fd, buffer, size );
} while( !quit );

However, independent of the buffer size there have been clicks in the files.
The read() has been either interrupted (too long) or blocked by a (too long)
write(). Now, since the out_fd cannot be set non-blocking (actually, it is
non-blocking), I put the write outside the recording loop which excludes the
possibly blocking write():

do {
  read( dsp_fd, large_buffer, huge_size );
} while( !quit );
write( out_fd, buffer, huge_size );

This fulfilled my dreams. But not all my dreams since the upper limit of
huge_size is just the size of the swap space. Thus, for recording a CD
5 swap partitions (of 128 MB) are needed. But even if the system swapped,
the recorded track has been ok.
With this result I delayed my work on cddarec 6 months ago: I was able
to record single tracks with it and I didn't need more. However, last week
a friend asked me to put a record to CD and I said yes for a simple reason
(the attentive reader knows it already):
"Even if the system swaps..." means that the sound data is actually written to
disk! Thus, the physical process of writing to disk cannot be the reason for
clicks. If even the kernel is able to write the data without interrupting the
recording (significantly), there must be a way to reach the same thing with
a user program that writes only the sound data!

The next try was to put the write into a thread. I hoped that raising the
thread takes less time in the recording loop that a direct write. Since the
main loop immediately continues reading after creating the write thread,
at least one additional buffer has been neccessary. This did the job much
better that the first version. However, not good enough. There still have
been clicks in the files.

After a lot of coffee has been drunken and a lot of cigarettes have been
smoked, I finally found that the system sync is responsible for the fatal
iterruptions. Obviously, the amount of data written to disk within one sync(2)
call is so large that it significantly disturbs the read(). Now, the
solution is quite simple (man, it took me hours!): The amount of data to
be sync'ed must be decreased -- just by calling sync(2) by myself after
every write()! Wow, this works fine even with my PIO ide drives:

write_thread() {
  write( out_fd, buffer[j], size );
  sync();
}
main() {
  do {
    read( dsp_fd, buffer[i], size );
    pthtead_detach();
    pthtead_create();
  }
  pthread_join();
  exit();
}

This is the current state of cddarec. One more word should be lost on
the buffer handling:
Assuming n>1 buffers to be allocated, after buffer0 is filled the next
read() is called for buffer1 and so on. Since the write thread will usually
take less time than the read() call, 2 buffers should be enough. Using a
single buffer only doesn't make sense when writing-while-reading is needed
(for longer tracks) since the next read() will much probably start to
overwrite the first bytes of the buffer before they can be accessed
by write(). Therefore I made the n=1 case singular in the way that the
main loop is executed only once, i.e. the buffer is filled and flushed
only once. This is for RAM recording -- the last resort.
Indepentent of the buffer size specified by the s-option, since the program
is blocked by the read() call, only hitting Ctrl-C helps to quit (abort!)
the program before the specified buffer is totally filled. With small buffers
this doesn't have much effect, but it does when using large buffers (for
RAM recording). Keep this in mind when launching a

cddarec -vvs 12:40:0 -n 1 > tmp.cdr

command -- hitting the q key doesn't stop recording.
Although this behaviour was not neccessary it has been kept for not making
the program even more singular. Furthermore, sampling to RAM shouldn't be
the default operation! (Good luck.)



2. Other cdda utilities
=======================

Cddarec is one of some simple cdda utilities (named cdda*), which
should be available at the sunsite ftp site (sunsite.unc.edu). Currently
available (at least on my disk :-) are:

cddack		-	infos on cdda files (time, peak levels, dc comp., ...)
cddagen		-	waveform generator (currently sine only)
cddamod		-	integer mods (muting & crossing channels, cutting)
cddaplay	-	playback cdda files
cddaproc	-	floating point mods using FOURIER transform (curr. non)
cddaread	-	cd -> file for ide drives -- no more cdda2wav!
cddarec		-	hd recording

Thu Feb 11 23:40:23 CET 1999
solyga@beast.absinth.net
