A band aid for plugin editor related crash. Something needs to be
[ardour.git] / libs / ardour / filesource.cc
index 0df4c29c3d117005ee04da965332351f1b763fb1..1f3a89557c50747a8981ade51c29bca0579b07fb 100644 (file)
@@ -65,6 +65,7 @@
 #include <ardour/filesource.h>
 #include <ardour/session.h>
 #include <ardour/cycle_timer.h>
+#include <ardour/pcm_utils.h>
 
 #include "i18n.h"
 
@@ -77,16 +78,40 @@ char   FileSource::bwf_organization_code[4] = "las";
 char   FileSource::bwf_serial_number[13] = "000000000000";
 string FileSource::search_path;
 
+#undef WE_ARE_BIGENDIAN
+#ifdef __BIG_ENDIAN__
+#define WE_ARE_BIGENDIAN true
+#else
+#define WE_ARE_BIGENDIAN false
+#endif
+
+#define Swap_32(value)         \
+       (((((uint32_t)value)<<24) & 0xFF000000) | \
+        ((((uint32_t)value)<< 8) & 0x00FF0000) | \
+        ((((uint32_t)value)>> 8) & 0x0000FF00) | \
+        ((((uint32_t)value)>>24) & 0x000000FF))
+
+#define Swap_16(value)         \
+       (((((uint16_t)value)>> 8) & 0x000000FF) | \
+        ((((uint16_t)value)<< 8) & 0x0000FF00))
+
+
 void
 FileSource::set_search_path (string p)
 {
        search_path = p;
 }
 
-FileSource::FileSource (string pathstr, jack_nframes_t rate, bool repair_first)
+FileSource::FileSource (string pathstr, jack_nframes_t rate, bool repair_first, SampleFormat samp_format)
 {
        /* constructor used when the file cannot already exist or might be damaged */
-
+       _sample_format = samp_format;
+       if (samp_format == FormatInt24) {
+               _sample_size = 3;
+       } else {
+               _sample_size = sizeof(float);
+       }
+       
        if (repair_first && repair (pathstr, rate)) {
                throw failed_constructor ();
        }
@@ -101,6 +126,9 @@ FileSource::FileSource (string pathstr, jack_nframes_t rate, bool repair_first)
 FileSource::FileSource (const XMLNode& node, jack_nframes_t rate) 
        : Source (node)
 {
+       _sample_format = FormatFloat;
+       _sample_size = sizeof(float);
+
        if (set_state (node)) {
                throw failed_constructor();
        }
@@ -158,13 +186,13 @@ FileSource::init (string pathstr, bool must_exist, jack_nframes_t rate)
                vector<string*>* result = scanner (search_path, regexp, false, true, -1);
                
                if (result == 0 || result->size() == 0) {
-                       error << compose (_("FileSource: \"%1\" not found when searching %2 using %3"), 
+                       error << string_compose (_("FileSource: \"%1\" not found when searching %2 using %3"), 
                                          pathstr, search_path, regexp) << endmsg;
                        goto out;
                }
                
                if (result->size() > 1) {
-                       string msg = compose (_("FileSource: \"%1\" is ambigous when searching %2\n\t"), pathstr, search_path);
+                       string msg = string_compose (_("FileSource: \"%1\" is ambigous when searching %2\n\t"), pathstr, search_path);
                        vector<string*>::iterator x = result->begin();
 
                        while (true) {
@@ -199,7 +227,7 @@ FileSource::init (string pathstr, bool must_exist, jack_nframes_t rate)
 
        if (access (_path.c_str(), F_OK) != 0) {
                if (must_exist) {
-                       error << compose(_("Filesource: cannot find required file (%1): %2"), _path, strerror (errno)) << endmsg;
+                       error << string_compose(_("Filesource: cannot find required file (%1): %2"), _path, strerror (errno)) << endmsg;
                        goto out;
                        
                }
@@ -207,13 +235,13 @@ FileSource::init (string pathstr, bool must_exist, jack_nframes_t rate)
                if (errno == ENOENT) {
                        new_file = true;
                } else {
-                       error << compose(_("Filesource: cannot check for existing file (%1): %2"), _path, strerror (errno)) << endmsg;
+                       error << string_compose(_("Filesource: cannot check for existing file (%1): %2"), _path, strerror (errno)) << endmsg;
                        goto out;
                }
        }
 
        if ((fd = open64 (_path.c_str(), O_RDWR|O_CREAT, 0644)) < 0) {
-               error << compose(_("FileSource: could not open \"%1\": (%2)"), _path, strerror (errno)) << endmsg;
+               error << string_compose(_("FileSource: could not open \"%1\": (%2)"), _path, strerror (errno)) << endmsg;
                goto out;
        }
        
@@ -239,7 +267,7 @@ FileSource::init (string pathstr, bool must_exist, jack_nframes_t rate)
                is_bwf = Config->get_native_format_is_bwf ();
 
                if (fill_header (rate)) {
-                       error << compose (_("FileSource: cannot write header in %1"), _path) << endmsg;
+                       error << string_compose (_("FileSource: cannot write header in %1"), _path) << endmsg;
                        goto out;
                }
 
@@ -254,17 +282,17 @@ FileSource::init (string pathstr, bool must_exist, jack_nframes_t rate)
        } else {
 
                if (discover_chunks (must_exist)) {
-                       error << compose (_("FileSource: cannot locate chunks in %1"), _path) << endmsg;
+                       error << string_compose (_("FileSource: cannot locate chunks in %1"), _path) << endmsg;
                        goto out;
                }
                
                if (read_header (must_exist)) {
-                       error << compose (_("FileSource: cannot read header in %1"), _path) << endmsg;
+                       error << string_compose (_("FileSource: cannot read header in %1"), _path) << endmsg;
                        goto out;
                }
 
                if (check_header (rate, must_exist)) {
-                       error << compose (_("FileSource: cannot check header in %1"), _path) << endmsg;
+                       error << string_compose (_("FileSource: cannot check header in %1"), _path) << endmsg;
                        goto out;
                }
 
@@ -272,7 +300,7 @@ FileSource::init (string pathstr, bool must_exist, jack_nframes_t rate)
        }
        
        if ((ret = initialize_peakfile (new_file, _path))) {
-               error << compose (_("FileSource: cannot initialize peakfile for %1"), _path) << endmsg;
+               error << string_compose (_("FileSource: cannot initialize peakfile for %1"), _path) << endmsg;
        }
 
   out:
@@ -313,7 +341,8 @@ FileSource::discover_chunks (bool silent)
        off64_t end;
        off64_t offset;
        char null_terminated_id[5];
-
+       bool doswap = false;
+       
        if ((end = lseek (fd, 0, SEEK_END)) < 0) {
                error << _("FileSource: cannot seek to end of file") << endmsg;
                return -1;
@@ -324,9 +353,15 @@ FileSource::discover_chunks (bool silent)
                return -1;
        }
 
-       if (memcmp (rw.id, "RIFF", 4) || memcmp (rw.text, "WAVE", 4)) {
+       if (memcmp (rw.id, "RIFF", 4) == 0 && memcmp (rw.text, "WAVE", 4) == 0) {
+               header.bigendian = false;
+       }
+       else if (memcmp(rw.id, "RIFX", 4) == 0 && memcmp (rw.text, "WAVE", 4) == 0) {
+               header.bigendian = true;
+       }
+       else {
                if (!silent) {
-                       error << compose (_("FileSource %1: not a RIFF/WAVE file"), _path) << endmsg;
+                       error << string_compose (_("FileSource %1: not a RIFF/WAVE file"), _path) << endmsg;
                }
                return -1;
        }
@@ -335,6 +370,14 @@ FileSource::discover_chunks (bool silent)
 
        /* OK, its a RIFF/WAVE file. Find each chunk */
 
+       doswap = header.bigendian != WE_ARE_BIGENDIAN;
+       
+       if (doswap) {
+               swap_endian(rw);
+       }
+
+       
+       
        memcpy (null_terminated_id, rw.id, 4);
        chunk_info.push_back (ChunkInfo (null_terminated_id, rw.size, 0));
 
@@ -349,7 +392,39 @@ FileSource::discover_chunks (bool silent)
                        return -1;
                }
 
+               if (doswap) {
+                       swap_endian(this_chunk);
+               }
+
                memcpy (null_terminated_id, this_chunk.id, 4);
+
+               /* do sanity check and possible correction to legacy ardour RIFF wavs
+                  created on big endian platforms. after swapping, the size field will not be
+                  in range for the fmt chunk
+               */
+               if ((memcmp(null_terminated_id, "fmt ", 4) == 0 || memcmp(null_terminated_id, "bext", 4) == 0)
+                    && !header.bigendian && (this_chunk.size > 700 || this_chunk.size < 0))
+               {
+                       warning << _("filesource: correcting mis-written RIFF file to become a RIFX: ") << name() << endmsg;
+                       
+                       memcpy (&rw.id, "RIFX", 4);
+                       ::pwrite64 (fd, &rw.id, 4, 0);
+                       header.bigendian = true;
+                       // fix wave chunk already read
+                       swap_endian(rw);
+                       
+                       doswap = header.bigendian != WE_ARE_BIGENDIAN;
+
+                       // now reset offset and continue the loop
+                       // to reread all the chunks
+                       chunk_info.clear();
+                       memcpy (null_terminated_id, rw.id, 4);
+                       chunk_info.push_back (ChunkInfo (null_terminated_id, rw.size, 0));
+                       offset = sizeof (rw);
+                       continue;
+               }
+                               
+
                if (end != 44)
                        if ((memcmp(null_terminated_id, "data", 4) == 0))
                                if ((this_chunk.size == 0) || (this_chunk.size > (end - offset)))
@@ -365,6 +440,44 @@ FileSource::discover_chunks (bool silent)
        return 0;
 }
 
+void
+FileSource::swap_endian (GenericChunk & chunk) const
+{
+       chunk.size = Swap_32(chunk.size);
+}
+
+void
+FileSource::swap_endian (FMTChunk & chunk) const
+{
+       chunk.size = Swap_32(chunk.size);
+
+       chunk.formatTag = Swap_16(chunk.formatTag);
+       chunk.nChannels = Swap_16(chunk.nChannels);
+       chunk.nSamplesPerSec = Swap_32(chunk.nSamplesPerSec);
+       chunk.nAvgBytesPerSec = Swap_32(chunk.nAvgBytesPerSec);
+       chunk.nBlockAlign = Swap_16(chunk.nBlockAlign);
+       chunk.nBitsPerSample = Swap_16(chunk.nBitsPerSample);
+}
+
+void
+FileSource::swap_endian (BroadcastChunk & chunk) const
+{
+       chunk.size = Swap_32(chunk.size);
+
+       chunk.time_reference_low = Swap_32(chunk.time_reference_low);
+       chunk.time_reference_high = Swap_32(chunk.time_reference_high);
+       chunk.version = Swap_16(chunk.version);
+}
+
+void FileSource::swap_endian (Sample *buf, jack_nframes_t cnt) const
+{
+       for (jack_nframes_t n=0; n < cnt; ++n) {
+               uint32_t * tmp = (uint32_t *) &buf[n];
+               *tmp = Swap_32(*tmp);
+       }
+}
+
+
 FileSource::ChunkInfo*
 FileSource::lookup_chunk (string what)
 {
@@ -380,8 +493,15 @@ int
 FileSource::fill_header (jack_nframes_t rate)
 {
        /* RIFF/WAVE */
-       
-       memcpy (header.wave.id, "RIFF", 4);
+
+       if (WE_ARE_BIGENDIAN) {
+               memcpy (header.wave.id, "RIFX", 4);
+               header.bigendian = true;
+       }
+       else {
+               memcpy (header.wave.id, "RIFF", 4);
+               header.bigendian = false;
+       }
        header.wave.size = 0; /* file size */
        memcpy (header.wave.text, "WAVE", 4);
 
@@ -401,11 +521,11 @@ FileSource::fill_header (jack_nframes_t rate)
                struct utsname utsinfo;
 
                if ((pwinfo = getpwuid (getuid())) == 0) {
-                       error << compose(_("FileSource: cannot get user information for BWF header (%1)"), strerror(errno)) << endmsg;
+                       error << string_compose(_("FileSource: cannot get user information for BWF header (%1)"), strerror(errno)) << endmsg;
                        return -1;
                }
                if (uname (&utsinfo)) {
-                       error << compose(_("FileSource: cannot get host information for BWF header (%1)"), strerror(errno)) << endmsg;
+                       error << string_compose(_("FileSource: cannot get host information for BWF header (%1)"), strerror(errno)) << endmsg;
                        return -1;
                }
 
@@ -448,12 +568,19 @@ FileSource::fill_header (jack_nframes_t rate)
        memcpy (header.format.id, "fmt ", 4);
        header.format.size = sizeof (FMTChunk) - sizeof (GenericChunk);
 
-       header.format.formatTag = 3; /* little-endian IEEE float format */
+       if (_sample_format == FormatInt24) {
+               header.format.formatTag = 1; // PCM
+               header.format.nBlockAlign = 3;
+               header.format.nBitsPerSample = 24;
+       }
+       else {
+               header.format.formatTag = 3; /* little-endian IEEE float format */
+               header.format.nBlockAlign = 4;
+               header.format.nBitsPerSample = 32;
+       }
        header.format.nChannels = 1; /* mono */
        header.format.nSamplesPerSec = rate;
        header.format.nAvgBytesPerSec = rate * sizeof (Sample);
-       header.format.nBlockAlign = 4;
-       header.format.nBitsPerSample = 32;
        
        /* DATA */
 
@@ -543,7 +670,7 @@ FileSource::update_header (jack_nframes_t when, struct tm& now, time_t tnow)
        compute_header_size ();
 
        if (write_header()) {
-               error << compose(_("FileSource[%1]: cannot update data size: %2"), _path, strerror (errno)) << endmsg;
+               error << string_compose(_("FileSource[%1]: cannot update data size: %2"), _path, strerror (errno)) << endmsg;
                return -1;
        }
 
@@ -558,15 +685,25 @@ FileSource::read_header (bool silent)
        /* we already have the chunk info, so just load up whatever we have */
 
        ChunkInfo* info;
-
-       if ((info = lookup_chunk ("RIFF")) == 0) {
+       
+       if (header.bigendian == false && (info = lookup_chunk ("RIFF")) == 0) {
                error << _("FileSource: can't find RIFF chunk info") << endmsg;
                return -1;
        }
-
+       else if (header.bigendian == true && (info = lookup_chunk ("RIFX")) == 0) {
+               error << _("FileSource: can't find RIFX chunk info") << endmsg;
+               return -1;
+       }
+       
+       
        /* just fill this chunk/header ourselves, disk i/o is stupid */
 
-       memcpy (header.wave.id, "RIFF", 4);
+       if (header.bigendian) {
+               memcpy (header.wave.id, "RIFX", 4);
+       }
+       else {
+               memcpy (header.wave.id, "RIFF", 4);
+       }
        header.wave.size = 0;
        memcpy (header.wave.text, "WAVE", 4);
 
@@ -594,6 +731,10 @@ FileSource::read_header (bool silent)
                return -1;
        }
 
+       if (header.bigendian != WE_ARE_BIGENDIAN) {
+               swap_endian (header.format);
+       }
+       
        if ((info = lookup_chunk ("data")) == 0) {
                error << _("FileSource: can't find data chunk info") << endmsg;
                return -1;
@@ -604,6 +745,10 @@ FileSource::read_header (bool silent)
                return -1;
        }
 
+       if (header.bigendian != WE_ARE_BIGENDIAN) {
+               swap_endian (header.data);
+       }
+
        return 0;
 }
 
@@ -613,11 +758,15 @@ FileSource::read_broadcast_data (ChunkInfo& info)
        int32_t coding_history_size;
 
        if (::pread (fd, (char *) &header.bext, sizeof (header.bext), info.offset + sizeof (GenericChunk)) != sizeof (header.bext)) {
-               error << compose(_("FileSource: cannot read Broadcast Wave data from existing audio file \"%1\" (%2)"),
+               error << string_compose(_("FileSource: cannot read Broadcast Wave data from existing audio file \"%1\" (%2)"),
                                 _path, strerror (errno)) << endmsg;
                return -1;
        }
 
+       if (header.bigendian != WE_ARE_BIGENDIAN) {
+               swap_endian (header.bext);
+       }
+       
        if (info.size > sizeof (header.bext)) {
 
                coding_history_size = info.size - (sizeof (header.bext) - sizeof (GenericChunk));
@@ -625,7 +774,7 @@ FileSource::read_broadcast_data (ChunkInfo& info)
                char data[coding_history_size];
                
                if (::pread (fd, data, coding_history_size, info.offset + sizeof (BroadcastChunk)) != coding_history_size) {
-                       error << compose(_("FileSource: cannot read Broadcast Wave coding history from audio file \"%1\" (%2)"),
+                       error << string_compose(_("FileSource: cannot read Broadcast Wave coding history from audio file \"%1\" (%2)"),
                                         _path, strerror (errno)) << endmsg;
                        return -1;
                }
@@ -656,9 +805,18 @@ FileSource::read_broadcast_data (ChunkInfo& info)
 int
 FileSource::check_header (jack_nframes_t rate, bool silent)
 {
-       if (header.format.formatTag != 3) { /* IEEE float */
+       if (header.format.formatTag == 1 && header.format.nBitsPerSample == 24) {
+               // 24 bit PCM
+               _sample_format = FormatInt24;
+               _sample_size = 3;
+       } else if (header.format.formatTag == 3) {
+               /* IEEE float */                
+               _sample_format = FormatFloat;
+               _sample_size = 4;
+       }
+       else {
                if (!silent) {
-                       error << compose(_("FileSource \"%1\" does not use floating point format.\n"   
+                       error << string_compose(_("FileSource \"%1\" does not use valid sample format.\n"   
                                           "This is probably a programming error."), _path) << endmsg;
                }
                return -1;
@@ -697,17 +855,17 @@ FileSource::check_header (jack_nframes_t rate, bool silent)
        }
 
        if (data_offset == 0) {
-               error << compose(_("FileSource \"%1\" has no \"data\" chunk"), _path) << endmsg;
+               error << string_compose(_("FileSource \"%1\" has no \"data\" chunk"), _path) << endmsg;
                return -1;
        }
 
        if (_length * sizeof (Sample) != (jack_nframes_t) header.data.size) {
-               warning << compose(_("%1: data length in header (%2) differs from implicit size in file (%3)"),
+               warning << string_compose(_("%1: data length in header (%2) differs from implicit size in file (%3)"),
                                   _path, header.data.size, _length * sizeof (Sample)) << endmsg;
        }
 
        if ((jack_nframes_t) header.format.nSamplesPerSec != rate) {
-               warning << compose(_("\"%1\" has a sample rate of %2 instead of %3 as used by this session"),
+               warning << string_compose(_("\"%1\" has a sample rate of %2 instead of %3 as used by this session"),
                                   _path, header.format.nSamplesPerSec, rate) << endmsg;
        }
 
@@ -722,9 +880,15 @@ FileSource::write_header()
        /* write RIFF/WAVE boilerplate */
 
        pos = 0;
+
+       WAVEChunk wchunk = header.wave;
+       if (header.bigendian != WE_ARE_BIGENDIAN) {
+               swap_endian(wchunk);
+       }
        
-       if (::pwrite64 (fd, (char *) &header.wave, sizeof (header.wave), pos) != sizeof (header.wave)) {
-               error << compose(_("FileSource: cannot write WAVE chunk: %1"), strerror (errno)) << endmsg;
+       if (::pwrite64 (fd, (char *) &wchunk, sizeof (wchunk), pos) != sizeof (wchunk)) {
+               error << string_compose(_("FileSource: cannot write WAVE chunk: %1"), strerror (errno)) << endmsg;
                return -1;
        }
        
@@ -734,7 +898,12 @@ FileSource::write_header()
 
                /* write broadcast chunk data without copy history */
 
-               if (::pwrite64 (fd, (char *) &header.bext, sizeof (header.bext), pos) != sizeof (header.bext)) {
+               BroadcastChunk bchunk = header.bext;
+               if (header.bigendian != WE_ARE_BIGENDIAN) {
+                       swap_endian (bchunk);
+               }
+               
+               if (::pwrite64 (fd, (char *) &bchunk, sizeof (bchunk), pos) != sizeof (bchunk)) {
                        return -1;
                }
 
@@ -757,16 +926,25 @@ FileSource::write_header()
        }
 
         /* write fmt and data chunks */
-
-       if (::pwrite64 (fd, (char *) &header.format, sizeof (header.format), pos) != sizeof (header.format)) {
-               error << compose(_("FileSource: cannot write format chunk: %1"), strerror (errno)) << endmsg;
+       FMTChunk fchunk = header.format;
+       if (header.bigendian != WE_ARE_BIGENDIAN) {
+               swap_endian (fchunk);
+       }
+       
+       if (::pwrite64 (fd, (char *) &fchunk, sizeof (fchunk), pos) != sizeof (fchunk)) {
+               error << string_compose(_("FileSource: cannot write format chunk: %1"), strerror (errno)) << endmsg;
                return -1;
        }
 
        pos += sizeof (header.format);
-       
-       if (::pwrite64 (fd, (char *) &header.data, sizeof (header.data), pos) != sizeof (header.data)) {
-               error << compose(_("FileSource: cannot data chunk: %1"), strerror (errno)) << endmsg;
+
+       GenericChunk dchunk = header.data;
+       if (header.bigendian != WE_ARE_BIGENDIAN) {
+               swap_endian (dchunk);
+       }
+       if (::pwrite64 (fd, (char *) &dchunk, sizeof (dchunk), pos) != sizeof (dchunk)) {
+               error << string_compose(_("FileSource: cannot data chunk: %1"), strerror (errno)) << endmsg;
                return -1;
        }
 
@@ -780,69 +958,38 @@ FileSource::mark_for_remove ()
 }
 
 jack_nframes_t
-FileSource::read (Sample *dst, jack_nframes_t start, jack_nframes_t cnt) const
+FileSource::read (Sample *dst, jack_nframes_t start, jack_nframes_t cnt, char * workbuf) const
 {
        LockMonitor lm (_lock, __LINE__, __FILE__);
-       return read_unlocked (dst, start, cnt);
+       return read_unlocked (dst, start, cnt, workbuf);
 }
 
 jack_nframes_t
-FileSource::read_unlocked (Sample *dst, jack_nframes_t start, jack_nframes_t cnt) const
+FileSource::read_unlocked (Sample *dst, jack_nframes_t start, jack_nframes_t cnt, char * workbuf) const
 {
-       int32_t byte_cnt;
-       int nread;
-
-       byte_cnt = cnt * sizeof (Sample);
-
-       if ((nread = pread (fd, (char *) dst, byte_cnt, data_offset + (start * sizeof (Sample)))) != (off64_t) byte_cnt) {
-               
-               cerr << "FileSource: \""
-                    << _path
-                    << "\" bad read at frame "
-                    << start
-                    << ", of "
-                    << cnt
-                    << " (bytes="
-                    << byte_cnt
-                    << ") frames [length = " << _length 
-                    << " eor = " << start + cnt << "] ("
-                    << strerror (errno)
-                    << ") (read "
-                    << nread / sizeof (Sample)
-                    << " (bytes=" <<nread
-                    << ")) pos was"
-                    << data_offset
-                    << '+'
-                    << start << '*' << sizeof(Sample)
-                    << " = " << data_offset + (start * sizeof(Sample))
-                    << endl;
-               
+       
+       if (file_read(dst, start, cnt, workbuf) != (ssize_t) cnt) {
                return 0;
        }
-
-       _read_data_count = byte_cnt;
-               
+       
        return cnt;
 }
 
 jack_nframes_t
-FileSource::write (Sample *data, jack_nframes_t cnt)
+FileSource::write (Sample *data, jack_nframes_t cnt, char * workbuf)
 {
        {
                LockMonitor lm (_lock, __LINE__, __FILE__);
                
-               int32_t byte_cnt = cnt * sizeof (Sample);
-               int32_t byte_pos = data_offset + (_length * sizeof (Sample));
                jack_nframes_t oldlen;
-
-               if (::pwrite64 (fd, (char *) data, byte_cnt, byte_pos) != (off64_t) byte_cnt) {
-                       error << compose(_("FileSource: \"%1\" bad write (%2)"), _path, strerror (errno)) << endmsg;
+               int32_t frame_pos = _length;
+               
+               if (file_write(data, frame_pos, cnt, workbuf) != (ssize_t) cnt) {
                        return 0;
                }
 
                oldlen = _length;
                _length += cnt;
-               _write_data_count = byte_cnt;
 
                if (_build_peakfiles) {
                        PeakBuildRecord *pbr = 0;
@@ -875,6 +1022,152 @@ FileSource::write (Sample *data, jack_nframes_t cnt)
        return cnt;
 }
 
+ssize_t
+FileSource::write_float(Sample *data, jack_nframes_t framepos, jack_nframes_t cnt, char * workbuf)
+{
+       int32_t byte_cnt = cnt * _sample_size;
+       int32_t byte_pos = data_offset + (framepos * _sample_size);
+       ssize_t retval;
+       
+       if ((retval = ::pwrite64 (fd, (char *) data, byte_cnt, byte_pos)) != (ssize_t) byte_cnt) {
+               error << string_compose(_("FileSource: \"%1\" bad write (%2)"), _path, strerror (errno)) << endmsg;
+               if (retval > 0) {
+                       return retval / _sample_size;
+               }
+               else {
+                       return retval;
+               }
+       }
+
+       _write_data_count = byte_cnt;
+       
+       return cnt;
+}
+
+ssize_t
+FileSource::read_float (Sample *dst, jack_nframes_t start, jack_nframes_t cnt, char * workbuf) const
+{
+       ssize_t nread;
+       ssize_t byte_cnt = (ssize_t) cnt * sizeof (Sample);
+       
+       if ((nread = pread (fd, (char *) dst, byte_cnt, data_offset + (start * _sample_size))) != byte_cnt) {
+               
+               cerr << "FileSource: \""
+                    << _path
+                    << "\" bad read at frame "
+                    << start
+                    << ", of "
+                    << cnt
+                    << " (bytes="
+                    << byte_cnt
+                    << ") frames [length = " << _length 
+                    << " eor = " << start + cnt << "] ("
+                    << strerror (errno)
+                    << ") (read "
+                    << nread / sizeof (Sample)
+                    << " (bytes=" <<nread
+                    << ")) pos was"
+                    << data_offset
+                    << '+'
+                    << start << '*' << sizeof(Sample)
+                    << " = " << data_offset + (start * sizeof(Sample))
+                    << endl;
+               
+               if (nread > 0) {
+                       return nread / _sample_size;
+               }
+               else {
+                       return nread;
+               }
+       }
+
+       if (header.bigendian != WE_ARE_BIGENDIAN) {
+               swap_endian(dst, cnt);
+       }
+       
+       _read_data_count = byte_cnt;
+
+       return cnt;
+}
+
+ssize_t
+FileSource::write_pcm_24(Sample *data, jack_nframes_t framepos, jack_nframes_t cnt, char * workbuf)
+{
+       int32_t byte_cnt = cnt * _sample_size;
+       int32_t byte_pos = data_offset + (framepos * _sample_size);
+       ssize_t retval;
+       
+       // convert to int24
+       if (header.bigendian) {
+               pcm_f2bet_clip_array (data, workbuf, cnt);
+       } else {
+               pcm_f2let_clip_array (data, workbuf, cnt);
+       }
+       
+       if ((retval = ::pwrite64 (fd, (char *) workbuf, byte_cnt, byte_pos)) != (ssize_t) byte_cnt) {
+               error << string_compose(_("FileSource: \"%1\" bad write (%2)"), _path, strerror (errno)) << endmsg;
+               if (retval > 0) {
+                       return retval / _sample_size;
+               }
+               else {
+                       return retval;
+               }
+       }
+
+       return (ssize_t) cnt;
+}
+
+ssize_t
+FileSource::read_pcm_24 (Sample *dst, jack_nframes_t start, jack_nframes_t cnt, char * workbuf) const
+{
+       ssize_t nread;
+       ssize_t byte_cnt = (ssize_t) cnt * _sample_size;
+
+       if ((nread = pread (fd, (char *) workbuf, byte_cnt, data_offset + (start * _sample_size))) != byte_cnt) {
+               
+               cerr << "FileSource: \""
+                    << _path
+                    << "\" bad 24bit read at frame "
+                    << start
+                    << ", of "
+                    << cnt
+                    << " (bytes="
+                    << byte_cnt
+                    << ") frames [length = " << _length 
+                    << " eor = " << start + cnt << "] ("
+                    << strerror (errno)
+                    << ") (read "
+                    << nread / sizeof (Sample)
+                    << " (bytes=" <<nread
+                    << ")) pos was"
+                    << data_offset
+                    << '+'
+                    << start << '*' << sizeof(Sample)
+                    << " = " << data_offset + (start * sizeof(Sample))
+                    << endl;
+
+               if (nread > 0) {
+                       return nread / _sample_size;
+               }
+               else {
+                       return nread;
+               }
+       }
+
+       // convert from 24bit->float
+       
+       if (header.bigendian) {
+               pcm_bet2f_array (workbuf, cnt, dst);
+       } else {
+               pcm_let2f_array (workbuf, cnt, dst);
+       }
+       
+       _read_data_count = byte_cnt;
+
+       return (ssize_t) cnt;
+}
+
+
 bool
 FileSource::is_empty (string path)
 {
@@ -960,7 +1253,7 @@ FileSource::move_to_trash (const string trash_dir_name)
                }
                
                if (version == 999) {
-                       error << compose (_("there are already 1000 files with names like %1; versioning discontinued"),
+                       error << string_compose (_("there are already 1000 files with names like %1; versioning discontinued"),
                                          newpath)
                              << endmsg;
                } else {
@@ -974,14 +1267,14 @@ FileSource::move_to_trash (const string trash_dir_name)
        }
 
        if (::rename (_path.c_str(), newpath.c_str()) != 0) {
-               error << compose (_("cannot rename audio file source from %1 to %2 (%3)"),
+               error << string_compose (_("cannot rename audio file source from %1 to %2 (%3)"),
                                  _path, newpath, strerror (errno))
                      << endmsg;
                return -1;
        }
 
        if (::unlink (peakpath.c_str()) != 0) {
-               error << compose (_("cannot remove peakfile %1 for %2 (%3)"),
+               error << string_compose (_("cannot remove peakfile %1 for %2 (%3)"),
                                  peakpath, _path, strerror (errno))
                      << endmsg;
                /* try to back out */
@@ -1023,7 +1316,9 @@ FileSource::repair (string path, jack_nframes_t rate)
        struct stat statbuf;
        size_t i;
        int ret = -1;
-
+       bool bigend = false;
+       bool doswap = false;
+       
        if (stat (path.c_str(), &statbuf)) {
                return -1;
        }
@@ -1043,15 +1338,25 @@ FileSource::repair (string path, jack_nframes_t rate)
                goto out;
        }
        
-       if (memcmp (&buf[0], "RIFF", 4) || memcmp (&buf[8], "WAVE", 4)) {
+       if (memcmp (&buf[0], "RIFF", 4) || memcmp (&buf[8], "WAVE", 4) || memcmp (&buf[0], "RIFX", 4)) {
                /* no header. too dangerous to proceed */
                goto out;
-               
        } 
 
+       if (memcmp (&buf[0], "RIFX", 4)==0) {
+               bigend = true;
+       }
+
+       doswap = bigend != WE_ARE_BIGENDIAN;
+       
        /* reset the size of the RIFF chunk header */
 
-       *((int32_t *)&buf[4]) = statbuf.st_size - 8;
+       if (doswap) {
+               *((int32_t *)&buf[4]) = Swap_32((int32_t)(statbuf.st_size - 8));
+       }
+       else {
+               *((int32_t *)&buf[4]) = statbuf.st_size - 8;
+       }
 
        /* find data chunk and reset the size */
 
@@ -1064,18 +1369,36 @@ FileSource::repair (string path, jack_nframes_t rate)
                        FMTChunk fmt;
 
                        memcpy (&fmt, ptr, sizeof (fmt));
+                       if (doswap) {
+                               swap_endian(fmt);
+                       }
+                       
                        fmt.nSamplesPerSec = rate;
                        fmt.nAvgBytesPerSec = rate * 4;
                        
                        /* put it back */
+                       if (doswap) {
+                               swap_endian(fmt);
+                       }
 
                        memcpy (ptr, &fmt, sizeof (fmt));
                        ptr += sizeof (fmt);
                        i += sizeof (fmt);
 
                } else if (memcmp (ptr, "data", 4) == 0) {
+                       GenericChunk dchunk;
+                       memcpy(&dchunk, ptr, sizeof(dchunk));
+
+                       if(doswap) {
+                               swap_endian(dchunk);
+                       }
+
+                       dchunk.size = statbuf.st_size - i - 8;
 
-                       *((int32_t *)&ptr[4]) = statbuf.st_size - i - 8;
+                       if (doswap) {
+                               swap_endian(dchunk);
+                       }
+                       memcpy (ptr, &dchunk, sizeof (dchunk));
                        break;
 
                } else {