"Run plugins while recording" -> "Do not run plugins while recording"
[ardour.git] / libs / ardour / filesource.cc
index 0df4c29c3d117005ee04da965332351f1b763fb1..fd74f66e76a404f83ba8713532c5bd5ddf9e99db 100644 (file)
@@ -77,6 +77,24 @@ char   FileSource::bwf_organization_code[4] = "las";
 char   FileSource::bwf_serial_number[13] = "000000000000";
 string FileSource::search_path;
 
+#undef WE_ARE_BIGENDIAN
+#ifdef WORDS_BIGENDIAN
+#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)
 {
@@ -158,13 +176,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 +217,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 +225,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 +257,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 +272,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 +290,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 +331,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 +343,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 +360,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 +382,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 +430,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 +483,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 +511,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;
                }
 
@@ -543,7 +653,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 +668,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 +714,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 +728,10 @@ FileSource::read_header (bool silent)
                return -1;
        }
 
+       if (header.bigendian != WE_ARE_BIGENDIAN) {
+               swap_endian (header.data);
+       }
+
        return 0;
 }
 
@@ -613,11 +741,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 +757,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;
                }
@@ -658,7 +790,7 @@ FileSource::check_header (jack_nframes_t rate, bool silent)
 {
        if (header.format.formatTag != 3) { /* IEEE float */
                if (!silent) {
-                       error << compose(_("FileSource \"%1\" does not use floating point format.\n"   
+                       error << string_compose(_("FileSource \"%1\" does not use floating point format.\n"   
                                           "This is probably a programming error."), _path) << endmsg;
                }
                return -1;
@@ -697,17 +829,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 +854,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 +872,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 +900,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;
        }
 
@@ -820,6 +972,10 @@ FileSource::read_unlocked (Sample *dst, jack_nframes_t start, jack_nframes_t cnt
                return 0;
        }
 
+       if (header.bigendian != WE_ARE_BIGENDIAN) {
+               swap_endian(dst, cnt);
+       }
+       
        _read_data_count = byte_cnt;
                
        return cnt;
@@ -836,7 +992,7 @@ FileSource::write (Sample *data, jack_nframes_t cnt)
                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;
+                       error << string_compose(_("FileSource: \"%1\" bad write (%2)"), _path, strerror (errno)) << endmsg;
                        return 0;
                }
 
@@ -960,7 +1116,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 +1130,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 +1179,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 +1201,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 +1232,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);
+                       }
 
-                       *((int32_t *)&ptr[4]) = statbuf.st_size - i - 8;
+                       dchunk.size = statbuf.st_size - i - 8;
+
+                       if (doswap) {
+                               swap_endian(dchunk);
+                       }
+                       memcpy (ptr, &dchunk, sizeof (dchunk));
                        break;
 
                } else {