| 
									
										
										
										
											2016-01-11 15:00:53 +00:00
										 |  |  | #include "BWFFile.hpp"
 | 
					
						
							| 
									
										
										
										
											2016-01-02 17:19:49 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | #include <cstring>
 | 
					
						
							|  |  |  | #include <numeric>
 | 
					
						
							| 
									
										
										
										
											2016-01-02 19:10:16 +00:00
										 |  |  | #include <algorithm>
 | 
					
						
							| 
									
										
										
										
											2016-01-02 17:19:49 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | #include <qendian.h>
 | 
					
						
							|  |  |  | #include <QAudioFormat>
 | 
					
						
							| 
									
										
										
										
											2016-01-11 15:00:43 +00:00
										 |  |  | #include <QDateTime>
 | 
					
						
							|  |  |  | #include <QDate>
 | 
					
						
							|  |  |  | #include <QTime>
 | 
					
						
							|  |  |  | #include <QString>
 | 
					
						
							|  |  |  | #include <QUuid>
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #include "pimpl_impl.hpp"
 | 
					
						
							| 
									
										
										
										
											2016-01-02 17:19:49 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-01-11 15:00:53 +00:00
										 |  |  | #include "moc_BWFFile.cpp"
 | 
					
						
							| 
									
										
										
										
											2016-01-02 17:19:49 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | namespace | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2016-01-11 15:00:43 +00:00
										 |  |  |   // chunk descriptor
 | 
					
						
							| 
									
										
										
										
											2016-01-02 17:19:49 +00:00
										 |  |  |   struct Desc | 
					
						
							|  |  |  |   { | 
					
						
							| 
									
										
										
										
											2016-06-10 15:54:16 +00:00
										 |  |  |     Desc () | 
					
						
							|  |  |  |       : size_ {0} | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-01-02 17:19:49 +00:00
										 |  |  |     explicit Desc (char const * id, quint32 size = 0) | 
					
						
							|  |  |  |       : size_ {size} | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |       set (id); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     void set (char const * id = nullptr) | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |       if (id) | 
					
						
							|  |  |  |         { | 
					
						
							| 
									
										
										
										
											2016-01-02 19:10:16 +00:00
										 |  |  |           auto len = std::min (size_t (4), strlen (id)); | 
					
						
							| 
									
										
										
										
											2016-01-02 17:19:49 +00:00
										 |  |  |           memcpy (id_.data (), id, len); | 
					
						
							| 
									
										
										
										
											2016-04-06 23:30:32 +00:00
										 |  |  |           if (len < 4u) | 
					
						
							|  |  |  |             { | 
					
						
							|  |  |  |               memset (id_.data () + len, ' ', 4u - len); | 
					
						
							|  |  |  |             } | 
					
						
							| 
									
										
										
										
											2016-01-02 17:19:49 +00:00
										 |  |  |         } | 
					
						
							|  |  |  |       else | 
					
						
							|  |  |  |         { | 
					
						
							|  |  |  |           memcpy (id_.data (), "JUNK", 4); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     void set (char const * id, quint32 size) | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |       set (id); | 
					
						
							|  |  |  |       size_ = size; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     char * operator & () {return reinterpret_cast<char *> (this);} | 
					
						
							| 
									
										
										
										
											2016-04-06 22:37:22 +00:00
										 |  |  |     char const * operator & () const {return reinterpret_cast<char const *> (this);} | 
					
						
							| 
									
										
										
										
											2016-01-02 17:19:49 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  |     std::array<char, 4> id_; | 
					
						
							|  |  |  |     quint32 size_; | 
					
						
							|  |  |  |   }; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-01-11 15:00:43 +00:00
										 |  |  |   // "fmt " chunk contents
 | 
					
						
							| 
									
										
										
										
											2016-01-02 17:19:49 +00:00
										 |  |  |   struct FormatChunk | 
					
						
							|  |  |  |   { | 
					
						
							|  |  |  |     quint16 audio_format; | 
					
						
							|  |  |  |     quint16 num_channels; | 
					
						
							|  |  |  |     quint32 sample_rate; | 
					
						
							|  |  |  |     quint32 byte_rate; | 
					
						
							|  |  |  |     quint16 block_align; | 
					
						
							|  |  |  |     quint16 bits_per_sample; | 
					
						
							|  |  |  |   }; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-01-11 15:00:43 +00:00
										 |  |  |   // "bext" chunk contents
 | 
					
						
							|  |  |  |   struct BroadcastAudioExtension | 
					
						
							|  |  |  |   { | 
					
						
							|  |  |  |     using Version = BWFFile::BextVersion; | 
					
						
							|  |  |  |     using UMID = BWFFile::UMID; | 
					
						
							| 
									
										
										
										
											2016-01-02 17:19:49 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-01-11 15:00:43 +00:00
										 |  |  |     BroadcastAudioExtension (Version version = Version::v_0) | 
					
						
							|  |  |  |       : version_ {static_cast<quint16> (version)} | 
					
						
							|  |  |  |       , umid_ {{}} | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |       // set some sensible defaults for the "bext" fields
 | 
					
						
							|  |  |  |       auto now = QDateTime::currentDateTimeUtc (); | 
					
						
							|  |  |  |       std::strncpy (origination_date_, | 
					
						
							|  |  |  |                     now.date ().toString ("yyyy-MM-dd").toLocal8Bit ().constData (), | 
					
						
							|  |  |  |                     sizeof origination_date_); | 
					
						
							|  |  |  |       std::strncpy (origination_time_, | 
					
						
							|  |  |  |                     now.time ().toString ("hh-mm-ss").toLocal8Bit ().constData (), | 
					
						
							|  |  |  |                     sizeof origination_time_); | 
					
						
							|  |  |  |       auto uuid = QUuid::createUuid ().toRfc4122 (); | 
					
						
							|  |  |  |       std::copy (uuid.cbegin (), uuid.cend (), umid_.data () + 16); | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2016-01-02 17:19:49 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-01-11 15:00:43 +00:00
										 |  |  |     char description_[256]; | 
					
						
							|  |  |  |     char originator_[32]; | 
					
						
							|  |  |  |     char originator_reference_[32]; | 
					
						
							|  |  |  |     char origination_date_[10]; | 
					
						
							|  |  |  |     char origination_time_[10]; | 
					
						
							|  |  |  |     quint32 time_reference_low_; | 
					
						
							|  |  |  |     quint32 time_reference_high_; | 
					
						
							|  |  |  |     quint16 version_; | 
					
						
							|  |  |  |     UMID umid_;                       // V1 zero for V0
 | 
					
						
							|  |  |  |     quint16 loudness_value_;          // V2
 | 
					
						
							|  |  |  |     quint16 loudness_range_;          // V2
 | 
					
						
							|  |  |  |     quint16 max_true_peak_level_;     // V2
 | 
					
						
							|  |  |  |     quint16 max_momentary_loudness_;  // V2
 | 
					
						
							|  |  |  |     quint16 max_short_term_loudness_; // V2
 | 
					
						
							|  |  |  |     quint8 reserved_[180]; | 
					
						
							|  |  |  |     char coding_history_[]; | 
					
						
							|  |  |  |   }; | 
					
						
							| 
									
										
										
										
											2016-01-02 17:19:49 +00:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-01-11 15:00:43 +00:00
										 |  |  | class BWFFile::impl final | 
					
						
							| 
									
										
										
										
											2016-01-02 17:19:49 +00:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2016-01-11 15:00:43 +00:00
										 |  |  | public: | 
					
						
							| 
									
										
										
										
											2016-06-09 23:39:33 +00:00
										 |  |  |   using FileError = QFile::FileError; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-01-11 15:00:43 +00:00
										 |  |  |   impl (QAudioFormat const& format) | 
					
						
							|  |  |  |     : header_dirty_ {true} | 
					
						
							|  |  |  |     , format_ {format} | 
					
						
							|  |  |  |     , header_length_ {-1} | 
					
						
							|  |  |  |     , data_size_ {-1} | 
					
						
							| 
									
										
										
										
											2016-06-09 23:39:33 +00:00
										 |  |  |     , error_ {FileError::NoError} | 
					
						
							| 
									
										
										
										
											2016-01-11 15:00:43 +00:00
										 |  |  |   { | 
					
						
							|  |  |  |   } | 
					
						
							| 
									
										
										
										
											2016-01-02 17:19:49 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-01-11 15:00:43 +00:00
										 |  |  |   impl (QAudioFormat const& format, QString const& name) | 
					
						
							|  |  |  |     : header_dirty_ {true} | 
					
						
							|  |  |  |     , format_ {format} | 
					
						
							|  |  |  |     , file_ {name} | 
					
						
							|  |  |  |     , header_length_ {-1} | 
					
						
							|  |  |  |     , data_size_ {-1} | 
					
						
							| 
									
										
										
										
											2016-06-09 23:39:33 +00:00
										 |  |  |     , error_ {FileError::NoError} | 
					
						
							| 
									
										
										
										
											2016-01-11 15:00:43 +00:00
										 |  |  |   { | 
					
						
							|  |  |  |   } | 
					
						
							| 
									
										
										
										
											2016-01-02 17:19:49 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-01-11 15:00:43 +00:00
										 |  |  |   impl (QAudioFormat const& format, QString const& name, InfoDictionary const& dictionary) | 
					
						
							|  |  |  |     : header_dirty_ {true} | 
					
						
							|  |  |  |     , format_ {format} | 
					
						
							|  |  |  |     , file_ {name} | 
					
						
							|  |  |  |     , info_dictionary_ {dictionary} | 
					
						
							|  |  |  |     , header_length_ {-1} | 
					
						
							|  |  |  |     , data_size_ {-1} | 
					
						
							| 
									
										
										
										
											2016-06-09 23:39:33 +00:00
										 |  |  |     , error_ {FileError::NoError} | 
					
						
							| 
									
										
										
										
											2016-01-11 15:00:43 +00:00
										 |  |  |   { | 
					
						
							|  |  |  |   } | 
					
						
							| 
									
										
										
										
											2016-01-02 17:19:49 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-01-11 15:00:43 +00:00
										 |  |  |   ~impl () | 
					
						
							|  |  |  |   { | 
					
						
							| 
									
										
										
										
											2016-06-09 23:39:33 +00:00
										 |  |  |     if (file_.isOpen ()) file_.close (); | 
					
						
							| 
									
										
										
										
											2016-01-11 15:00:43 +00:00
										 |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   bool initialize (BWFFile * self, OpenMode mode); | 
					
						
							|  |  |  |   bool read_header (); | 
					
						
							|  |  |  |   bool write_header (QAudioFormat); | 
					
						
							|  |  |  |   bool update_header (); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   BroadcastAudioExtension const * bext () const | 
					
						
							|  |  |  |   { | 
					
						
							|  |  |  |     return bext_.isEmpty () ? nullptr : reinterpret_cast<BroadcastAudioExtension const *> (bext_.constData ()); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   BroadcastAudioExtension * bext () | 
					
						
							|  |  |  |   { | 
					
						
							|  |  |  |     if (bext_.isEmpty ())       // create a "bext" chunk in place
 | 
					
						
							|  |  |  |       { | 
					
						
							|  |  |  |         bext_.data (); | 
					
						
							|  |  |  |         bext_.fill ('\0', sizeof (BroadcastAudioExtension)); | 
					
						
							|  |  |  |         new (bext_.data ()) BroadcastAudioExtension {}; | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |     return reinterpret_cast<BroadcastAudioExtension *> (bext_.data ()); | 
					
						
							|  |  |  |   } | 
					
						
							| 
									
										
										
										
											2016-01-02 17:19:49 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-01-11 15:00:43 +00:00
										 |  |  |   bool header_dirty_; | 
					
						
							|  |  |  |   QAudioFormat format_; | 
					
						
							|  |  |  |   QFile file_; | 
					
						
							|  |  |  |   QByteArray bext_; | 
					
						
							|  |  |  |   InfoDictionary info_dictionary_; | 
					
						
							|  |  |  |   qint64 header_length_; | 
					
						
							|  |  |  |   qint64 data_size_; | 
					
						
							| 
									
										
										
										
											2016-06-09 23:39:33 +00:00
										 |  |  |   FileError error_; | 
					
						
							| 
									
										
										
										
											2016-01-11 15:00:43 +00:00
										 |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | bool BWFFile::impl::initialize (BWFFile * self, OpenMode mode) | 
					
						
							| 
									
										
										
										
											2016-01-02 17:19:49 +00:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2016-01-11 15:00:43 +00:00
										 |  |  |   bool result {false}; | 
					
						
							|  |  |  |   if (mode & Append) | 
					
						
							| 
									
										
										
										
											2016-01-02 17:19:49 +00:00
										 |  |  |     { | 
					
						
							|  |  |  |       result = file_.seek (file_.size ()); | 
					
						
							| 
									
										
										
										
											2016-01-11 15:00:43 +00:00
										 |  |  |       if (result) result = self->seek (file_.size () - header_length_); | 
					
						
							| 
									
										
										
										
											2016-01-02 17:19:49 +00:00
										 |  |  |     } | 
					
						
							|  |  |  |   else | 
					
						
							|  |  |  |     { | 
					
						
							| 
									
										
										
										
											2016-01-11 15:00:43 +00:00
										 |  |  |       result = self->seek (0); | 
					
						
							| 
									
										
										
										
											2016-01-02 17:19:49 +00:00
										 |  |  |     } | 
					
						
							|  |  |  |   return result; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-01-11 15:00:43 +00:00
										 |  |  | bool BWFFile::impl::read_header () | 
					
						
							| 
									
										
										
										
											2016-01-02 17:19:49 +00:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2016-01-11 15:00:43 +00:00
										 |  |  |   header_length_ = -1; | 
					
						
							|  |  |  |   data_size_ = -1; | 
					
						
							|  |  |  |   if (!(file_.openMode () & ReadOnly)) return false; | 
					
						
							| 
									
										
										
										
											2016-01-02 17:19:49 +00:00
										 |  |  |   if (!file_.seek (0)) return false; | 
					
						
							|  |  |  |   Desc outer_desc; | 
					
						
							| 
									
										
										
										
											2016-01-02 19:10:16 +00:00
										 |  |  |   quint32 outer_offset = file_.pos (); | 
					
						
							| 
									
										
										
										
											2016-01-02 17:19:49 +00:00
										 |  |  |   quint32 outer_size {0}; | 
					
						
							|  |  |  |   bool be {false}; | 
					
						
							|  |  |  |   while (outer_offset < sizeof outer_desc + outer_desc.size_ - 1) // allow for uncounted pad
 | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |       if (file_.read (&outer_desc, sizeof outer_desc) != sizeof outer_desc) return false; | 
					
						
							|  |  |  |       be = !memcmp (&outer_desc.id_, "RIFX", 4); | 
					
						
							|  |  |  |       outer_size = be ? qFromBigEndian<quint32> (outer_desc.size_) : qFromLittleEndian<quint32> (outer_desc.size_); | 
					
						
							|  |  |  |       if (!memcmp (&outer_desc.id_, "RIFF", 4) || be) | 
					
						
							|  |  |  |         { | 
					
						
							|  |  |  |           // RIFF or RIFX
 | 
					
						
							|  |  |  |           char riff_item[4]; | 
					
						
							|  |  |  |           if (file_.read (riff_item, sizeof riff_item) != sizeof riff_item) return false; | 
					
						
							|  |  |  |           if (!memcmp (riff_item, "WAVE", 4)) | 
					
						
							|  |  |  |             { | 
					
						
							|  |  |  |               // WAVE
 | 
					
						
							|  |  |  |               Desc wave_desc; | 
					
						
							| 
									
										
										
										
											2016-01-02 19:10:16 +00:00
										 |  |  |               quint32 wave_offset = file_.pos (); | 
					
						
							| 
									
										
										
										
											2016-01-02 17:19:49 +00:00
										 |  |  |               quint32 wave_size {0}; | 
					
						
							|  |  |  |               while (wave_offset < outer_offset + sizeof outer_desc + outer_size - 1) | 
					
						
							|  |  |  |                 { | 
					
						
							|  |  |  |                   if (file_.read (&wave_desc, sizeof wave_desc) != sizeof wave_desc) return false; | 
					
						
							|  |  |  |                   wave_size = be ? qFromBigEndian<quint32> (wave_desc.size_) : qFromLittleEndian<quint32> (wave_desc.size_); | 
					
						
							| 
									
										
										
										
											2016-01-11 15:00:43 +00:00
										 |  |  |                   if (!memcmp (&wave_desc.id_, "bext", 4)) | 
					
						
							|  |  |  |                     { | 
					
						
							|  |  |  |                       bext_ = file_.read (wave_size); | 
					
						
							|  |  |  |                     } | 
					
						
							| 
									
										
										
										
											2016-01-02 17:19:49 +00:00
										 |  |  |                   if (!memcmp (&wave_desc.id_, "fmt ", 4)) | 
					
						
							|  |  |  |                     { | 
					
						
							|  |  |  |                       FormatChunk fmt; | 
					
						
							|  |  |  |                       if (file_.read (reinterpret_cast<char *> (&fmt), sizeof fmt) != sizeof fmt) return false; | 
					
						
							|  |  |  |                       auto audio_format = be ? qFromBigEndian<quint16> (fmt.audio_format) : qFromLittleEndian<quint16> (fmt.audio_format); | 
					
						
							|  |  |  |                       if (audio_format != 0 && audio_format != 1) return false; // not PCM nor undefined
 | 
					
						
							|  |  |  |                       format_.setByteOrder (be ? QAudioFormat::BigEndian : QAudioFormat::LittleEndian); | 
					
						
							|  |  |  |                       format_.setChannelCount (be ? qFromBigEndian<quint16> (fmt.num_channels) : qFromLittleEndian<quint16> (fmt.num_channels)); | 
					
						
							|  |  |  |                       format_.setCodec ("audio/pcm"); | 
					
						
							|  |  |  |                       format_.setSampleRate (be ? qFromBigEndian<quint32> (fmt.sample_rate) : qFromLittleEndian<quint32> (fmt.sample_rate)); | 
					
						
							|  |  |  |                       int bits_per_sample {be ? qFromBigEndian<quint16> (fmt.bits_per_sample) : qFromLittleEndian<quint16> (fmt.bits_per_sample)}; | 
					
						
							|  |  |  |                       format_.setSampleSize (bits_per_sample); | 
					
						
							|  |  |  |                       format_.setSampleType (8 == bits_per_sample ? QAudioFormat::UnSignedInt : QAudioFormat::SignedInt); | 
					
						
							|  |  |  |                     } | 
					
						
							|  |  |  |                   else if (!memcmp (&wave_desc.id_, "data", 4)) | 
					
						
							|  |  |  |                     { | 
					
						
							| 
									
										
										
										
											2016-01-11 15:00:43 +00:00
										 |  |  |                       data_size_ = wave_size; | 
					
						
							| 
									
										
										
										
											2016-01-02 17:19:49 +00:00
										 |  |  |                       header_length_ = file_.pos (); | 
					
						
							|  |  |  |                     } | 
					
						
							|  |  |  |                   else if (!memcmp (&wave_desc.id_, "LIST", 4)) | 
					
						
							|  |  |  |                     { | 
					
						
							|  |  |  |                       char list_type[4]; | 
					
						
							|  |  |  |                       if (file_.read (list_type, sizeof list_type) != sizeof list_type) return false; | 
					
						
							|  |  |  |                       if (!memcmp (list_type, "INFO", 4)) | 
					
						
							|  |  |  |                         { | 
					
						
							|  |  |  |                           Desc info_desc; | 
					
						
							| 
									
										
										
										
											2016-01-02 19:10:16 +00:00
										 |  |  |                           quint32 info_offset = file_.pos (); | 
					
						
							| 
									
										
										
										
											2016-01-02 17:19:49 +00:00
										 |  |  |                           quint32 info_size {0}; | 
					
						
							|  |  |  |                           while (info_offset < wave_offset + sizeof wave_desc + wave_size - 1) | 
					
						
							|  |  |  |                             { | 
					
						
							|  |  |  |                               if (file_.read (&info_desc, sizeof info_desc) != sizeof info_desc) return false; | 
					
						
							|  |  |  |                               info_size = be ? qFromBigEndian<quint32> (info_desc.size_) : qFromLittleEndian<quint32> (info_desc.size_); | 
					
						
							|  |  |  |                               info_dictionary_[info_desc.id_] = file_.read (info_size); | 
					
						
							|  |  |  |                               if (!file_.seek (info_offset + sizeof info_desc + (info_size + 1) / 2 * 2)) return false;; | 
					
						
							|  |  |  |                               info_offset = file_.pos (); | 
					
						
							|  |  |  |                             } | 
					
						
							|  |  |  |                         } | 
					
						
							|  |  |  |                     } | 
					
						
							|  |  |  |                   if (!file_.seek (wave_offset + sizeof wave_desc + (wave_size + 1) / 2 * 2)) return false; | 
					
						
							|  |  |  |                   wave_offset = file_.pos (); | 
					
						
							|  |  |  |                 } | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |       if (!file_.seek (outer_offset + sizeof outer_desc + (outer_size + 1) / 2 * 2)) return false; | 
					
						
							|  |  |  |       outer_offset = file_.pos (); | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2016-01-11 15:00:43 +00:00
										 |  |  |   return data_size_ >= 0 && file_.seek (header_length_); | 
					
						
							| 
									
										
										
										
											2016-01-02 17:19:49 +00:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-01-11 15:00:43 +00:00
										 |  |  | bool BWFFile::impl::write_header (QAudioFormat format) | 
					
						
							| 
									
										
										
										
											2016-01-02 17:19:49 +00:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2016-01-11 15:00:43 +00:00
										 |  |  |   data_size_ = -1; | 
					
						
							| 
									
										
										
										
											2016-01-02 17:19:49 +00:00
										 |  |  |   if ("audio/pcm" != format.codec ()) return false; | 
					
						
							| 
									
										
										
										
											2016-01-11 15:00:43 +00:00
										 |  |  |   if (!(file_.openMode () & WriteOnly)) return false; | 
					
						
							| 
									
										
										
										
											2016-01-02 17:19:49 +00:00
										 |  |  |   if (!file_.seek (0)) return false; | 
					
						
							|  |  |  |   header_length_ = 0; | 
					
						
							|  |  |  |   bool be {QAudioFormat::BigEndian == format_.byteOrder ()}; | 
					
						
							|  |  |  |   Desc desc {be ? "RIFX" : "RIFF"}; | 
					
						
							|  |  |  |   if (file_.write (&desc, sizeof desc) != sizeof desc) return false; | 
					
						
							|  |  |  |   header_dirty_ = true; | 
					
						
							|  |  |  |   if (file_.write ("WAVE", 4) != 4) return false; | 
					
						
							| 
									
										
										
										
											2016-01-11 15:00:43 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-01-02 17:19:49 +00:00
										 |  |  |   FormatChunk fmt; | 
					
						
							|  |  |  |   if (be) | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |       fmt.audio_format = qToBigEndian<quint16> (1); // PCM
 | 
					
						
							|  |  |  |       fmt.num_channels = qToBigEndian<quint16> (format.channelCount ()); | 
					
						
							|  |  |  |       fmt.sample_rate = qToBigEndian<quint32> (format.sampleRate ()); | 
					
						
							| 
									
										
										
										
											2016-01-19 19:06:12 +00:00
										 |  |  |       fmt.byte_rate = qToBigEndian<quint32> (format.bytesForDuration (1000000)); | 
					
						
							| 
									
										
										
										
											2016-01-02 17:19:49 +00:00
										 |  |  |       fmt.block_align = qToBigEndian<quint16> (format.bytesPerFrame ()); | 
					
						
							|  |  |  |       fmt.bits_per_sample = qToBigEndian<quint16> (format.sampleSize ()); | 
					
						
							|  |  |  |       desc.set ("fmt", qToBigEndian<quint32> (sizeof fmt)); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |   else | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |       fmt.audio_format = qToLittleEndian<quint16> (1); // PCM
 | 
					
						
							|  |  |  |       fmt.num_channels = qToLittleEndian<quint16> (format.channelCount ()); | 
					
						
							|  |  |  |       fmt.sample_rate = qToLittleEndian<quint32> (format.sampleRate ()); | 
					
						
							| 
									
										
										
										
											2016-01-19 19:06:12 +00:00
										 |  |  |       fmt.byte_rate = qToLittleEndian<quint32> (format.bytesForDuration (1000000)); | 
					
						
							| 
									
										
										
										
											2016-01-02 17:19:49 +00:00
										 |  |  |       fmt.block_align = qToLittleEndian<quint16> (format.bytesPerFrame ()); | 
					
						
							|  |  |  |       fmt.bits_per_sample = qToLittleEndian<quint16> (format.sampleSize ()); | 
					
						
							|  |  |  |       desc.set ("fmt", qToLittleEndian<quint32> (sizeof fmt)); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |   if (file_.write (&desc, sizeof desc) != sizeof desc) return false; | 
					
						
							|  |  |  |   if (file_.write (reinterpret_cast<char const *> (&fmt), sizeof fmt) != sizeof fmt) return false; | 
					
						
							| 
									
										
										
										
											2016-01-11 15:00:43 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  |   desc.set ("data"); | 
					
						
							|  |  |  |   if (file_.write (&desc, sizeof desc) != sizeof desc) return false; | 
					
						
							|  |  |  |   header_length_ = file_.pos (); | 
					
						
							|  |  |  |   return true; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | bool BWFFile::impl::update_header () | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |   if (header_length_ < 0 || !(file_.openMode () & WriteOnly)) return false; | 
					
						
							|  |  |  |   auto position = file_.pos (); | 
					
						
							|  |  |  |   bool be {QAudioFormat::BigEndian == format_.byteOrder ()}; | 
					
						
							|  |  |  |   Desc desc; | 
					
						
							|  |  |  |   auto size = data_size_ < 0 ? file_.size () - header_length_ : data_size_; | 
					
						
							|  |  |  |   if (!file_.seek (header_length_ - sizeof desc)) return false; | 
					
						
							|  |  |  |   desc.set ("data", be ? qToBigEndian<quint32> (size) : qToLittleEndian<quint32> (size)); | 
					
						
							|  |  |  |   if (file_.write (&desc, sizeof desc) != sizeof desc) return false; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   if (!bext_.isEmpty ()) | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |       if (!file_.seek (file_.size ())) return false; | 
					
						
							|  |  |  |       auto size = bext_.size (); | 
					
						
							|  |  |  |       desc.set ("bext", be ? qToBigEndian<quint32> (size) : qToLittleEndian<quint32> (size)); | 
					
						
							|  |  |  |       if ((file_.size () % 2) && file_.write ("\0", 1) != 1) return false; | 
					
						
							|  |  |  |       if (file_.write (&desc, sizeof desc) != sizeof desc) return false; | 
					
						
							|  |  |  |       auto * data = reinterpret_cast<BroadcastAudioExtension *> (bext_.data ()); | 
					
						
							|  |  |  |       if (be) | 
					
						
							|  |  |  |         { | 
					
						
							|  |  |  |           data->time_reference_low_ = qToBigEndian<quint32> (data->time_reference_low_); | 
					
						
							|  |  |  |           data->time_reference_high_ = qToBigEndian<quint32> (data->time_reference_high_); | 
					
						
							|  |  |  |           switch (static_cast<BextVersion> (data->version_)) | 
					
						
							|  |  |  |             { | 
					
						
							|  |  |  |             case BextVersion::v_0: | 
					
						
							|  |  |  |               data->version_ = qToBigEndian<quint32> (data->version_); | 
					
						
							|  |  |  |             default: | 
					
						
							|  |  |  |               data->loudness_value_ = qToBigEndian<quint16> (data->loudness_value_); | 
					
						
							|  |  |  |               data->loudness_range_ = qToBigEndian<quint16> (data->loudness_range_); | 
					
						
							|  |  |  |               data->max_true_peak_level_ = qToBigEndian<quint16> (data->max_true_peak_level_); | 
					
						
							|  |  |  |               data->max_momentary_loudness_ = qToBigEndian<quint16> (data->max_momentary_loudness_); | 
					
						
							|  |  |  |               data->max_short_term_loudness_ = qToBigEndian<quint16> (data->max_short_term_loudness_); | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |       else | 
					
						
							|  |  |  |         { | 
					
						
							|  |  |  |           data->time_reference_low_ = qToLittleEndian<quint32> (data->time_reference_low_); | 
					
						
							|  |  |  |           data->time_reference_high_ = qToLittleEndian<quint32> (data->time_reference_high_); | 
					
						
							|  |  |  |           switch (static_cast<BextVersion> (data->version_)) | 
					
						
							|  |  |  |             { | 
					
						
							|  |  |  |             case BextVersion::v_0: | 
					
						
							|  |  |  |               data->version_ = qToLittleEndian<quint32> (data->version_); | 
					
						
							|  |  |  |             default: | 
					
						
							|  |  |  |               data->loudness_value_ = qToLittleEndian<quint16> (data->loudness_value_); | 
					
						
							|  |  |  |               data->loudness_range_ = qToLittleEndian<quint16> (data->loudness_range_); | 
					
						
							|  |  |  |               data->max_true_peak_level_ = qToLittleEndian<quint16> (data->max_true_peak_level_); | 
					
						
							|  |  |  |               data->max_momentary_loudness_ = qToLittleEndian<quint16> (data->max_momentary_loudness_); | 
					
						
							|  |  |  |               data->max_short_term_loudness_ = qToLittleEndian<quint16> (data->max_short_term_loudness_); | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |       if (file_.write (bext_) != size) return false; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-01-02 17:19:49 +00:00
										 |  |  |   if (info_dictionary_.size ()) | 
					
						
							|  |  |  |     { | 
					
						
							| 
									
										
										
										
											2016-01-11 15:00:43 +00:00
										 |  |  |       if (!file_.seek (file_.size ())) return false; | 
					
						
							| 
									
										
										
										
											2016-01-02 17:19:49 +00:00
										 |  |  |       desc.set ("LIST"); | 
					
						
							| 
									
										
										
										
											2016-01-11 15:00:43 +00:00
										 |  |  |       if ((file_.size () % 2) && file_.write ("\0", 1) != 1) return false; | 
					
						
							| 
									
										
										
										
											2016-01-02 17:19:49 +00:00
										 |  |  |       if (file_.write (&desc, sizeof desc) != sizeof desc) return false; | 
					
						
							| 
									
										
										
										
											2016-01-11 15:00:43 +00:00
										 |  |  |       auto list_start = file_.pos (); | 
					
						
							| 
									
										
										
										
											2016-01-02 17:19:49 +00:00
										 |  |  |       if (file_.write ("INFO", 4) != 4) return false; | 
					
						
							|  |  |  |       for (auto iter = info_dictionary_.constBegin () | 
					
						
							|  |  |  |              ; iter != info_dictionary_.constEnd (); ++iter) | 
					
						
							|  |  |  |         { | 
					
						
							|  |  |  |           auto value = iter.value (); | 
					
						
							| 
									
										
										
										
											2016-01-11 15:00:43 +00:00
										 |  |  |           auto len = value.size () + 1; // include terminating null char
 | 
					
						
							| 
									
										
										
										
											2016-01-02 17:19:49 +00:00
										 |  |  |           desc.set (iter.key ().data (), be ? qToBigEndian<quint32> (len) : qToLittleEndian<quint32> (len)); | 
					
						
							| 
									
										
										
										
											2016-01-11 15:00:43 +00:00
										 |  |  |           if ((file_.size () % 2) && file_.write ("\0", 1) != 1) return false; | 
					
						
							| 
									
										
										
										
											2016-01-02 17:19:49 +00:00
										 |  |  |           if (file_.write (&desc, sizeof desc) != sizeof desc) return false; | 
					
						
							| 
									
										
										
										
											2016-01-11 15:00:43 +00:00
										 |  |  |           if (file_.write (value.constData (), len) != len) return false; | 
					
						
							| 
									
										
										
										
											2016-01-02 17:19:49 +00:00
										 |  |  |         } | 
					
						
							| 
									
										
										
										
											2016-01-11 15:00:43 +00:00
										 |  |  |       auto size = file_.pos () - list_start; | 
					
						
							|  |  |  |       if (!file_.seek (list_start - sizeof desc)) return false; | 
					
						
							|  |  |  |       desc.set ("LIST", be ? qToBigEndian<quint32> (size) : qToLittleEndian<quint32> (size)); | 
					
						
							| 
									
										
										
										
											2016-01-02 17:19:49 +00:00
										 |  |  |       if (file_.write (&desc, sizeof desc) != sizeof desc) return false; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   size = file_.size () - sizeof desc; | 
					
						
							| 
									
										
										
										
											2016-01-11 15:00:43 +00:00
										 |  |  |   if ((file_.size () % 2) && file_.seek (file_.size ()) && file_.write ("\0", 1) != 1) return false; | 
					
						
							|  |  |  |   if (!file_.seek (0)) return false; | 
					
						
							|  |  |  |   desc.set (be ? "RIFX" : "RIFF", be ? qToBigEndian<quint32> (size) : qToLittleEndian<quint32> (size)); | 
					
						
							| 
									
										
										
										
											2016-01-02 17:19:49 +00:00
										 |  |  |   if (file_.write (&desc, sizeof desc) != sizeof desc) return false; | 
					
						
							|  |  |  |   return file_.seek (position); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-01-11 15:00:43 +00:00
										 |  |  | //
 | 
					
						
							|  |  |  | // BWFFile implementation
 | 
					
						
							|  |  |  | //
 | 
					
						
							|  |  |  | BWFFile::BWFFile (QAudioFormat const& format, QObject * parent) | 
					
						
							|  |  |  |   : QIODevice {parent} | 
					
						
							|  |  |  |   , m_ {format} | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | BWFFile::BWFFile (QAudioFormat const& format, QString const& name, QObject * parent) | 
					
						
							|  |  |  |   : QIODevice {parent} | 
					
						
							|  |  |  |   , m_ {format, name} | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | BWFFile::BWFFile (QAudioFormat const& format, QString const& name | 
					
						
							|  |  |  |                   , InfoDictionary const& dictionary, QObject * parent) | 
					
						
							|  |  |  |   : QIODevice {parent} | 
					
						
							|  |  |  |   , m_ {format, name, dictionary} | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | BWFFile::~BWFFile () | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |   if (isOpen ()) close (); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | bool BWFFile::open (OpenMode mode) | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2016-06-09 23:39:33 +00:00
										 |  |  |   bool success {false}; | 
					
						
							| 
									
										
										
										
											2016-01-11 15:00:43 +00:00
										 |  |  |   if (!(mode & WriteOnly)) | 
					
						
							|  |  |  |     { | 
					
						
							| 
									
										
										
										
											2016-06-09 23:39:33 +00:00
										 |  |  |       if (!(success = m_->file_.open (mode & ~Text))) | 
					
						
							|  |  |  |         { | 
					
						
							|  |  |  |           setErrorString (m_->file_.errorString ()); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |       else | 
					
						
							|  |  |  |         { | 
					
						
							|  |  |  |           if (!(success = m_->read_header ())) | 
					
						
							|  |  |  |             { | 
					
						
							|  |  |  |               if (FileError::NoError == m_->file_.error ()) | 
					
						
							|  |  |  |                 { | 
					
						
							|  |  |  |                   setErrorString ("Unable to read file header"); | 
					
						
							|  |  |  |                   m_->error_ = FileError::ReadError; | 
					
						
							|  |  |  |                 } | 
					
						
							|  |  |  |               else | 
					
						
							|  |  |  |                 { | 
					
						
							|  |  |  |                   setErrorString (m_->file_.errorString ()); | 
					
						
							|  |  |  |                 } | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |         } | 
					
						
							| 
									
										
										
										
											2016-01-11 15:00:43 +00:00
										 |  |  |     } | 
					
						
							|  |  |  |   else | 
					
						
							|  |  |  |     { | 
					
						
							| 
									
										
										
										
											2016-06-09 23:39:33 +00:00
										 |  |  |       if (!(success = m_->file_.open (mode & ~Text))) | 
					
						
							| 
									
										
										
										
											2016-01-11 15:00:43 +00:00
										 |  |  |         { | 
					
						
							| 
									
										
										
										
											2016-06-09 23:39:33 +00:00
										 |  |  |           setErrorString (m_->file_.errorString ()); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |       else | 
					
						
							|  |  |  |         { | 
					
						
							|  |  |  |           if (!(success = (m_->read_header () || m_->write_header (m_->format_)))) | 
					
						
							|  |  |  |             { | 
					
						
							|  |  |  |               if (FileError::NoError == m_->file_.error ()) | 
					
						
							|  |  |  |                 { | 
					
						
							|  |  |  |                   setErrorString ("Unable to update file header"); | 
					
						
							|  |  |  |                   m_->error_ = FileError::WriteError; | 
					
						
							|  |  |  |                 } | 
					
						
							|  |  |  |               else | 
					
						
							|  |  |  |                 { | 
					
						
							|  |  |  |                   setErrorString (m_->file_.errorString ()); | 
					
						
							|  |  |  |                 } | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |           else if (!(success = m_->file_.resize (m_->header_length_))) | 
					
						
							| 
									
										
										
										
											2016-01-11 15:00:43 +00:00
										 |  |  |             { | 
					
						
							| 
									
										
										
										
											2016-06-09 23:39:33 +00:00
										 |  |  |               setErrorString (m_->file_.errorString ()); | 
					
						
							| 
									
										
										
										
											2016-01-11 15:00:43 +00:00
										 |  |  |             } | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2016-06-09 23:39:33 +00:00
										 |  |  |   success && (success = QIODevice::open (mode | Unbuffered)); | 
					
						
							|  |  |  |   if (success && !(success = m_->initialize (this, mode))) | 
					
						
							| 
									
										
										
										
											2016-01-11 15:00:43 +00:00
										 |  |  |     { | 
					
						
							| 
									
										
										
										
											2016-06-09 23:39:33 +00:00
										 |  |  |       if (FileError::NoError == m_->file_.error ()) | 
					
						
							|  |  |  |         { | 
					
						
							|  |  |  |           setErrorString ("Unable to initialize file header"); | 
					
						
							|  |  |  |           m_->error_ = FileError::WriteError; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |       else | 
					
						
							|  |  |  |         { | 
					
						
							|  |  |  |           setErrorString (m_->file_.errorString ()); | 
					
						
							|  |  |  |         } | 
					
						
							| 
									
										
										
										
											2016-01-11 15:00:43 +00:00
										 |  |  |     } | 
					
						
							| 
									
										
										
										
											2016-06-09 23:39:33 +00:00
										 |  |  |   if (!success) close (); | 
					
						
							|  |  |  |   return success; | 
					
						
							| 
									
										
										
										
											2016-01-11 15:00:43 +00:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | bool BWFFile::open(FILE * fh, OpenMode mode, FileHandleFlags flags) | 
					
						
							| 
									
										
										
										
											2016-01-02 17:19:49 +00:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2016-06-09 23:39:33 +00:00
										 |  |  |   bool success {false}; | 
					
						
							|  |  |  |   if (!(mode & ReadOnly)) | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |       setErrorString ("Read or read/write access must be specified"); | 
					
						
							|  |  |  |       m_->error_ = FileError::OpenError; | 
					
						
							|  |  |  |       return success; | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2016-04-07 00:02:24 +00:00
										 |  |  |   if (!(mode & WriteOnly)) | 
					
						
							| 
									
										
										
										
											2016-01-11 15:00:43 +00:00
										 |  |  |     { | 
					
						
							| 
									
										
										
										
											2016-06-09 23:39:33 +00:00
										 |  |  |       if (!(success = m_->file_.open (fh, mode & ~Text, flags))) | 
					
						
							|  |  |  |         { | 
					
						
							|  |  |  |           setErrorString (m_->file_.errorString ()); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |       else | 
					
						
							|  |  |  |         { | 
					
						
							|  |  |  |           if (!(success = m_->read_header ())) | 
					
						
							|  |  |  |             { | 
					
						
							|  |  |  |               if (FileError::NoError == m_->file_.error ()) | 
					
						
							|  |  |  |                 { | 
					
						
							|  |  |  |                   setErrorString ("Unable to read file header"); | 
					
						
							|  |  |  |                   m_->error_ = FileError::ReadError; | 
					
						
							|  |  |  |                 } | 
					
						
							|  |  |  |               else | 
					
						
							|  |  |  |                 { | 
					
						
							|  |  |  |                   setErrorString (m_->file_.errorString ()); | 
					
						
							|  |  |  |                 } | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |         } | 
					
						
							| 
									
										
										
										
											2016-01-11 15:00:43 +00:00
										 |  |  |     } | 
					
						
							|  |  |  |   else | 
					
						
							|  |  |  |     { | 
					
						
							| 
									
										
										
										
											2016-06-09 23:39:33 +00:00
										 |  |  |       if (!(success = m_->file_.open (fh, mode & ~Text, flags))) | 
					
						
							| 
									
										
										
										
											2016-01-11 15:00:43 +00:00
										 |  |  |         { | 
					
						
							| 
									
										
										
										
											2016-06-09 23:39:33 +00:00
										 |  |  |           setErrorString (m_->file_.errorString ()); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |       else | 
					
						
							|  |  |  |         { | 
					
						
							|  |  |  |           if (!(success = (m_->read_header () || m_->write_header (m_->format_)))) | 
					
						
							|  |  |  |             { | 
					
						
							|  |  |  |               if (FileError::NoError == m_->file_.error ()) | 
					
						
							|  |  |  |                 { | 
					
						
							|  |  |  |                   setErrorString ("Unable to update file header"); | 
					
						
							|  |  |  |                   m_->error_ = FileError::WriteError; | 
					
						
							|  |  |  |                 } | 
					
						
							|  |  |  |               else | 
					
						
							|  |  |  |                 { | 
					
						
							|  |  |  |                   setErrorString (m_->file_.errorString ()); | 
					
						
							|  |  |  |                 } | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |           else if (!(success = m_->file_.resize (m_->header_length_))) | 
					
						
							| 
									
										
										
										
											2016-01-11 15:00:43 +00:00
										 |  |  |             { | 
					
						
							| 
									
										
										
										
											2016-06-09 23:39:33 +00:00
										 |  |  |               setErrorString (m_->file_.errorString ()); | 
					
						
							| 
									
										
										
										
											2016-01-11 15:00:43 +00:00
										 |  |  |             } | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2016-06-09 23:39:33 +00:00
										 |  |  |   success && (success = QIODevice::open (mode | Unbuffered)); | 
					
						
							|  |  |  |   if (success && !(success = m_->initialize (this, mode))) | 
					
						
							| 
									
										
										
										
											2016-01-11 15:00:43 +00:00
										 |  |  |     { | 
					
						
							| 
									
										
										
										
											2016-06-09 23:39:33 +00:00
										 |  |  |       if (FileError::NoError == m_->file_.error ()) | 
					
						
							|  |  |  |         { | 
					
						
							|  |  |  |           setErrorString ("Unable to initialize file header"); | 
					
						
							|  |  |  |           m_->error_ = FileError::WriteError; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |       else | 
					
						
							|  |  |  |         { | 
					
						
							|  |  |  |           setErrorString (m_->file_.errorString ()); | 
					
						
							|  |  |  |         } | 
					
						
							| 
									
										
										
										
											2016-01-11 15:00:43 +00:00
										 |  |  |     } | 
					
						
							| 
									
										
										
										
											2016-06-09 23:39:33 +00:00
										 |  |  |   if (!success) close (); | 
					
						
							|  |  |  |   return success; | 
					
						
							| 
									
										
										
										
											2016-01-11 15:00:43 +00:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | bool BWFFile::open (int fd, OpenMode mode, FileHandleFlags flags) | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2016-06-09 23:39:33 +00:00
										 |  |  |   bool success {false}; | 
					
						
							|  |  |  |   if (!(mode & ReadOnly)) | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |       setErrorString ("Read or read/write access must be specified"); | 
					
						
							|  |  |  |       m_->error_ = FileError::OpenError; | 
					
						
							|  |  |  |       return success; | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2016-01-11 15:00:43 +00:00
										 |  |  |   if (!(mode & WriteOnly)) | 
					
						
							|  |  |  |     { | 
					
						
							| 
									
										
										
										
											2016-06-09 23:39:33 +00:00
										 |  |  |       if (!(success = m_->file_.open (fd, mode & ~Text, flags))) | 
					
						
							|  |  |  |         { | 
					
						
							|  |  |  |           setErrorString (m_->file_.errorString ()); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |       else | 
					
						
							|  |  |  |         { | 
					
						
							|  |  |  |           if (!(success = m_->read_header ())) | 
					
						
							|  |  |  |             { | 
					
						
							|  |  |  |               if (FileError::NoError == m_->file_.error ()) | 
					
						
							|  |  |  |                 { | 
					
						
							|  |  |  |                   setErrorString ("Unable to read file header"); | 
					
						
							|  |  |  |                   m_->error_ = FileError::ReadError; | 
					
						
							|  |  |  |                 } | 
					
						
							|  |  |  |               else | 
					
						
							|  |  |  |                 { | 
					
						
							|  |  |  |                   setErrorString (m_->file_.errorString ()); | 
					
						
							|  |  |  |                 } | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |         } | 
					
						
							| 
									
										
										
										
											2016-01-11 15:00:43 +00:00
										 |  |  |     } | 
					
						
							|  |  |  |   else | 
					
						
							|  |  |  |     { | 
					
						
							| 
									
										
										
										
											2016-06-09 23:39:33 +00:00
										 |  |  |       if (!(success = m_->file_.open (fd, mode & ~Text, flags))) | 
					
						
							| 
									
										
										
										
											2016-01-11 15:00:43 +00:00
										 |  |  |         { | 
					
						
							| 
									
										
										
										
											2016-06-09 23:39:33 +00:00
										 |  |  |           setErrorString (m_->file_.errorString ()); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |       else | 
					
						
							|  |  |  |         { | 
					
						
							|  |  |  |           if (!(success = (m_->read_header () || m_->write_header (m_->format_)))) | 
					
						
							| 
									
										
										
										
											2016-01-11 15:00:43 +00:00
										 |  |  |             { | 
					
						
							| 
									
										
										
										
											2016-06-09 23:39:33 +00:00
										 |  |  |               if (FileError::NoError == m_->file_.error ()) | 
					
						
							|  |  |  |                 { | 
					
						
							|  |  |  |                   setErrorString ("Unable to update file header"); | 
					
						
							|  |  |  |                   m_->error_ = FileError::WriteError; | 
					
						
							|  |  |  |                 } | 
					
						
							|  |  |  |               else | 
					
						
							|  |  |  |                 { | 
					
						
							|  |  |  |                   setErrorString (m_->file_.errorString ()); | 
					
						
							|  |  |  |                 } | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |           else if (!(success = m_->file_.resize (m_->header_length_))) | 
					
						
							|  |  |  |             { | 
					
						
							|  |  |  |               setErrorString (m_->file_.errorString ()); | 
					
						
							| 
									
										
										
										
											2016-01-11 15:00:43 +00:00
										 |  |  |             } | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2016-06-09 23:39:33 +00:00
										 |  |  |   success && (success = QIODevice::open (mode | Unbuffered)); | 
					
						
							|  |  |  |   if (success && !(success = m_->initialize (this, mode))) | 
					
						
							| 
									
										
										
										
											2016-01-11 15:00:43 +00:00
										 |  |  |     { | 
					
						
							| 
									
										
										
										
											2016-06-09 23:39:33 +00:00
										 |  |  |       if (FileError::NoError == m_->file_.error ()) | 
					
						
							|  |  |  |         { | 
					
						
							|  |  |  |           setErrorString ("Unable to initialize file header"); | 
					
						
							|  |  |  |           m_->error_ = FileError::WriteError; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |       else | 
					
						
							|  |  |  |         { | 
					
						
							|  |  |  |           setErrorString (m_->file_.errorString ()); | 
					
						
							|  |  |  |         } | 
					
						
							| 
									
										
										
										
											2016-01-11 15:00:43 +00:00
										 |  |  |     } | 
					
						
							| 
									
										
										
										
											2016-06-09 23:39:33 +00:00
										 |  |  |   if (!success) close (); | 
					
						
							|  |  |  |   return success; | 
					
						
							| 
									
										
										
										
											2016-01-11 15:00:43 +00:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | QAudioFormat const& BWFFile::format () const {return m_->format_;} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | auto BWFFile::list_info () -> InfoDictionary& | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |   m_->header_dirty_ = true; | 
					
						
							|  |  |  |   return m_->info_dictionary_; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // Broadcast Audio Extension fields
 | 
					
						
							|  |  |  | auto BWFFile::bext_version () const -> BextVersion | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |   return static_cast<BextVersion> (m_->bext () ? 0 : m_->bext ()->version_); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void BWFFile::bext_version (BextVersion version) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |   m_->header_dirty_ = true; | 
					
						
							|  |  |  |   m_->bext ()->version_ = static_cast<quint16> (version); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | QByteArray BWFFile::bext_description () const | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |   if (!m_->bext ()) return {}; | 
					
						
							|  |  |  |   return QByteArray::fromRawData (m_->bext ()->description_, strlen (m_->bext ()->description_)); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void BWFFile::bext_description (QByteArray const& description) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |   m_->header_dirty_ = true; | 
					
						
							|  |  |  |   std::strncpy (m_->bext ()->description_, description.constData (), sizeof (BroadcastAudioExtension::description_)); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | QByteArray BWFFile::bext_originator () const | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |   if (!m_->bext ()) return {}; | 
					
						
							|  |  |  |   return QByteArray::fromRawData (m_->bext ()->originator_, strlen (m_->bext ()->originator_)); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void BWFFile::bext_originator (QByteArray const& originator) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |   m_->header_dirty_ = true; | 
					
						
							|  |  |  |   std::strncpy (m_->bext ()->originator_, originator.constData (), sizeof (BroadcastAudioExtension::originator_)); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | QByteArray BWFFile::bext_originator_reference () const | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |   if (!m_->bext ()) return {}; | 
					
						
							|  |  |  |   return QByteArray::fromRawData (m_->bext ()->originator_reference_, strlen (m_->bext ()->originator_reference_)); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void BWFFile::bext_originator_reference (QByteArray const& reference) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |   m_->header_dirty_ = true; | 
					
						
							|  |  |  |   std::strncpy (m_->bext ()->originator_reference_, reference.constData (), sizeof (BroadcastAudioExtension::originator_reference_)); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | QDateTime BWFFile::bext_origination_date_time () const | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |   if (!m_->bext ()) return {}; | 
					
						
							|  |  |  |   return {QDate::fromString (m_->bext ()->origination_date_, "yyyy-MM-dd"), | 
					
						
							|  |  |  |       QTime::fromString (m_->bext ()->origination_time_, "hh-mm-ss"), Qt::UTC}; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void BWFFile::bext_origination_date_time (QDateTime const& dt) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |   m_->header_dirty_ = true; | 
					
						
							|  |  |  |   std::strncpy (m_->bext ()->origination_date_, | 
					
						
							|  |  |  |                 dt.date ().toString ("yyyy-MM-dd").toLocal8Bit ().constData (), | 
					
						
							|  |  |  |                 sizeof (BroadcastAudioExtension::origination_date_)); | 
					
						
							|  |  |  |   std::strncpy (m_->bext ()->origination_time_, | 
					
						
							|  |  |  |                 dt.time ().toString ("hh-mm-ss").toLocal8Bit ().constData (), | 
					
						
							|  |  |  |                 sizeof (BroadcastAudioExtension::origination_time_)); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | quint64 BWFFile::bext_time_reference () const | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |   if (!m_->bext ()) return 0; | 
					
						
							|  |  |  |   return (quint64 (m_->bext ()->time_reference_high_) << 32) + m_->bext ()->time_reference_low_; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void BWFFile::bext_time_reference (quint64 time_code) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |   m_->header_dirty_ = true; | 
					
						
							|  |  |  |   m_->bext ()->time_reference_low_ = time_code & 0x00000000ffffffffll; | 
					
						
							|  |  |  |   m_->bext ()->time_reference_high_ = time_code >> 32; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | auto BWFFile::bext_umid () const -> UMID | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2016-01-20 20:32:30 +00:00
										 |  |  |   UMID umid {{'\0'}}; | 
					
						
							| 
									
										
										
										
											2016-01-11 15:00:43 +00:00
										 |  |  |   if (m_->bext ()) | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |       umid = m_->bext ()->umid_; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |   return umid; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void BWFFile::bext_umid (UMID const& umid) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |   m_->header_dirty_ = true; | 
					
						
							|  |  |  |   m_->bext ()->umid_ = umid; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | quint16 BWFFile::bext_loudness_value () const | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |   if (!m_->bext ()) return 0; | 
					
						
							|  |  |  |   return m_->bext ()->loudness_value_; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void BWFFile::bext_loudness_value (quint16 value) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |   m_->header_dirty_ = true; | 
					
						
							|  |  |  |   m_->bext ()->loudness_value_ = value; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | quint16 BWFFile::bext_loudness_range () const | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |   if (!m_->bext ()) return 0; | 
					
						
							|  |  |  |   return m_->bext ()->loudness_range_; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void BWFFile::bext_loudness_range (quint16 range) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |   m_->header_dirty_ = true; | 
					
						
							|  |  |  |   m_->bext ()->loudness_range_ = range; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | quint16 BWFFile::bext_max_true_peak_level () const | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |   if (!m_->bext ()) return 0; | 
					
						
							|  |  |  |   return m_->bext ()->max_true_peak_level_; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void BWFFile::bext_max_true_peak_level (quint16 level) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |   m_->header_dirty_ = true; | 
					
						
							|  |  |  |   m_->bext ()->max_true_peak_level_ = level; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | quint16 BWFFile::bext_max_momentary_loudness () const | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |   if (!m_->bext ()) return 0; | 
					
						
							|  |  |  |   return m_->bext ()->max_momentary_loudness_; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void BWFFile::bext_max_momentary_loudness (quint16 loudness) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |   m_->header_dirty_ = true; | 
					
						
							|  |  |  |   m_->bext ()->max_momentary_loudness_ = loudness; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | quint16 BWFFile::bext_max_short_term_loudness () const | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |   if (!m_->bext ()) return 0; | 
					
						
							|  |  |  |   return m_->bext ()->max_short_term_loudness_; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void BWFFile::bext_max_short_term_loudness (quint16 loudness) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |   m_->header_dirty_ = true; | 
					
						
							|  |  |  |   m_->bext ()->max_short_term_loudness_ = loudness; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | QByteArray BWFFile::bext_coding_history () const | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |   if (size_t (m_->bext_.size ()) <= sizeof (BroadcastAudioExtension)) return {}; | 
					
						
							|  |  |  |   return QByteArray::fromRawData (m_->bext ()->coding_history_, | 
					
						
							|  |  |  |                                   m_->bext_.size () - sizeof (BroadcastAudioExtension)); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void BWFFile::bext_coding_history (QByteArray const& text) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |   m_->header_dirty_ = true; | 
					
						
							|  |  |  |   m_->bext ();                  // ensure we have a correctly
 | 
					
						
							|  |  |  |                                 // initialized m_->bext_
 | 
					
						
							|  |  |  |   auto length = std::min (strlen (text.constData ()), size_t (text.size ())); | 
					
						
							|  |  |  |   m_->bext_.resize (sizeof (BroadcastAudioExtension) + length); | 
					
						
							|  |  |  |   std::strncpy (m_->bext ()->coding_history_, text.constData (), length); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | bool BWFFile::reset () | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2016-06-09 23:39:33 +00:00
										 |  |  |   bool success {true}; | 
					
						
							| 
									
										
										
										
											2016-01-11 15:00:43 +00:00
										 |  |  |   if (m_->file_.isOpen ()) | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |       m_->info_dictionary_.clear (); | 
					
						
							|  |  |  |       m_->bext_.clear (); | 
					
						
							|  |  |  |       auto size = m_->data_size_ < 0 ? m_->file_.size () - m_->header_length_ : m_->data_size_; | 
					
						
							|  |  |  |       m_->data_size_ = size; | 
					
						
							| 
									
										
										
										
											2016-01-20 20:32:30 +00:00
										 |  |  |       if (m_->header_length_ > qint64 (3 * sizeof (Desc) + 4 + sizeof (FormatChunk))) | 
					
						
							| 
									
										
										
										
											2016-01-11 15:00:43 +00:00
										 |  |  |         { | 
					
						
							|  |  |  |           // we need to move the data down
 | 
					
						
							|  |  |  |           auto old_pos = m_->header_length_; | 
					
						
							| 
									
										
										
										
											2016-06-09 23:39:33 +00:00
										 |  |  |           if (!(success = m_->write_header (m_->format_))) | 
					
						
							| 
									
										
										
										
											2016-01-11 15:00:43 +00:00
										 |  |  |             { | 
					
						
							| 
									
										
										
										
											2016-06-09 23:39:33 +00:00
										 |  |  |               auto new_pos = m_->header_length_; | 
					
						
							|  |  |  |               QByteArray buffer; | 
					
						
							|  |  |  |               while (size) | 
					
						
							|  |  |  |                 { | 
					
						
							|  |  |  |                   success = m_->file_.seek (old_pos); | 
					
						
							|  |  |  |                   if (!success) | 
					
						
							|  |  |  |                     { | 
					
						
							|  |  |  |                       setErrorString (m_->file_.errorString ()); | 
					
						
							|  |  |  |                     } | 
					
						
							|  |  |  |                   else | 
					
						
							|  |  |  |                     { | 
					
						
							|  |  |  |                       buffer = m_->file_.read (std::min (size, qint64 (32768))); | 
					
						
							|  |  |  |                       success = m_->file_.seek (new_pos); | 
					
						
							|  |  |  |                       if (!success) | 
					
						
							|  |  |  |                         { | 
					
						
							|  |  |  |                           setErrorString (m_->file_.errorString ()); | 
					
						
							|  |  |  |                         } | 
					
						
							|  |  |  |                       else | 
					
						
							|  |  |  |                         { | 
					
						
							|  |  |  |                           qint64 bytes = m_->file_.write (buffer); | 
					
						
							|  |  |  |                           if (bytes < 0) | 
					
						
							|  |  |  |                             { | 
					
						
							|  |  |  |                               setErrorString (m_->file_.errorString ()); | 
					
						
							|  |  |  |                               success = false; | 
					
						
							|  |  |  |                             } | 
					
						
							|  |  |  |                           else | 
					
						
							|  |  |  |                             { | 
					
						
							|  |  |  |                               new_pos += buffer.size (); | 
					
						
							|  |  |  |                               old_pos += buffer.size (); | 
					
						
							|  |  |  |                               size -= buffer.size (); | 
					
						
							|  |  |  |                             } | 
					
						
							|  |  |  |                         } | 
					
						
							|  |  |  |                     } | 
					
						
							|  |  |  |                 } | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |           else | 
					
						
							|  |  |  |             { | 
					
						
							|  |  |  |               if (FileError::NoError == m_->file_.error ()) | 
					
						
							|  |  |  |                 { | 
					
						
							|  |  |  |                   setErrorString ("Unable to update file header"); | 
					
						
							|  |  |  |                   m_->error_ = FileError::WriteError; | 
					
						
							|  |  |  |                 } | 
					
						
							|  |  |  |               else | 
					
						
							|  |  |  |                 { | 
					
						
							|  |  |  |                   setErrorString (m_->file_.errorString ()); | 
					
						
							|  |  |  |                 } | 
					
						
							|  |  |  |               return success; | 
					
						
							| 
									
										
										
										
											2016-01-11 15:00:43 +00:00
										 |  |  |             } | 
					
						
							|  |  |  |         } | 
					
						
							| 
									
										
										
										
											2016-06-09 23:39:33 +00:00
										 |  |  |       if (success) | 
					
						
							|  |  |  |         { | 
					
						
							|  |  |  |           success = m_->file_.resize (m_->header_length_ + m_->data_size_); | 
					
						
							|  |  |  |           if (!success) | 
					
						
							|  |  |  |             { | 
					
						
							|  |  |  |               setErrorString (m_->file_.errorString ()); | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |           m_->header_dirty_ = true; | 
					
						
							|  |  |  |         } | 
					
						
							| 
									
										
										
										
											2016-01-11 15:00:43 +00:00
										 |  |  |     } | 
					
						
							| 
									
										
										
										
											2016-06-09 23:39:33 +00:00
										 |  |  |   else | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |       setErrorString ("File not open"); | 
					
						
							|  |  |  |       m_->error_ = FileError::WriteError; | 
					
						
							|  |  |  |       success = false; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |   return success && QIODevice::reset (); | 
					
						
							| 
									
										
										
										
											2016-01-02 17:19:49 +00:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-01-11 15:00:43 +00:00
										 |  |  | qint64 BWFFile::size () const | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |   return m_->data_size_ < 0 ? m_->file_.size () - m_->header_length_ : m_->data_size_; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | bool BWFFile::isSequential () const | 
					
						
							| 
									
										
										
										
											2016-01-02 17:19:49 +00:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2016-01-11 15:00:43 +00:00
										 |  |  |   return m_->file_.isSequential (); | 
					
						
							| 
									
										
										
										
											2016-01-02 17:19:49 +00:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-01-11 15:00:43 +00:00
										 |  |  | void BWFFile::close () | 
					
						
							| 
									
										
										
										
											2016-01-02 17:19:49 +00:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2016-06-09 23:39:33 +00:00
										 |  |  |   if (isOpen ()) QIODevice::close (); | 
					
						
							|  |  |  |   if (m_->header_dirty_ || m_->data_size_ < 0) | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |       m_->update_header (); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |   if (m_->file_.isOpen ()) | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |       m_->file_.close (); | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2016-01-02 17:19:49 +00:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-01-11 15:00:43 +00:00
										 |  |  | bool BWFFile::seek (qint64 pos) | 
					
						
							| 
									
										
										
										
											2016-01-02 17:19:49 +00:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2016-06-09 23:39:33 +00:00
										 |  |  |   if (pos < 0) | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |       setErrorString ("Attempt to seek before beginning of data"); | 
					
						
							|  |  |  |       m_->error_ = FileError::PositionError; | 
					
						
							|  |  |  |       return false; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |   bool success = QIODevice::seek (pos); | 
					
						
							|  |  |  |   if (success) | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |       success = m_->file_.seek (pos + m_->header_length_); | 
					
						
							|  |  |  |       if (!success) setErrorString (m_->file_.errorString ()); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |   return success; | 
					
						
							| 
									
										
										
										
											2016-01-02 17:19:49 +00:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-01-11 15:00:43 +00:00
										 |  |  | qint64 BWFFile::readData (char * data, qint64 max_size) | 
					
						
							| 
									
										
										
										
											2016-01-02 17:19:49 +00:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2016-01-11 15:00:43 +00:00
										 |  |  |   return m_->file_.read (data, max_size); | 
					
						
							| 
									
										
										
										
											2016-01-02 17:19:49 +00:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-01-11 15:00:43 +00:00
										 |  |  | qint64 BWFFile::writeData (char const* data, qint64 max_size) | 
					
						
							| 
									
										
										
										
											2016-01-02 17:19:49 +00:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2016-01-11 15:00:43 +00:00
										 |  |  |   auto bytes = m_->file_.write (data, max_size); | 
					
						
							|  |  |  |   if (bytes > 0 && atEnd ()) | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |       m_->header_dirty_ = true; | 
					
						
							|  |  |  |       m_->data_size_ = -1; | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2016-01-02 17:19:49 +00:00
										 |  |  |   return bytes; | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2016-01-11 15:00:43 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | // forward to QFile
 | 
					
						
							|  |  |  | bool BWFFile::copy (QString const& new_name) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |   close (); | 
					
						
							|  |  |  |   return m_->file_.copy (new_name); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | bool BWFFile::exists () const {return m_->file_.exists ();} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | bool BWFFile::link (QString const& link_name) {return m_->file_.link (link_name);} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | bool BWFFile::remove () | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |   close (); | 
					
						
							|  |  |  |   return m_->file_.remove (); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | bool BWFFile::rename (QString const& new_name) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |   close (); | 
					
						
							|  |  |  |   return m_->file_.rename (new_name); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void BWFFile::setFileName (QString const& name) {m_->file_.setFileName (name);} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | QString BWFFile::symLinkTarget () const {return m_->file_.symLinkTarget ();} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | QString BWFFile::fileName () const {return m_->file_.fileName ();} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | auto BWFFile::permissions () const -> Permissions {return m_->file_.permissions ();} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | bool BWFFile::resize (qint64 new_size) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |   auto size = m_->file_.size (); | 
					
						
							|  |  |  |   if (pos () > new_size) seek (new_size); | 
					
						
							|  |  |  |   auto result = m_->file_.resize (m_->header_length_ + new_size); | 
					
						
							|  |  |  |   if (m_->data_size_ >= 0) | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |       // set any fresh bytes to zero
 | 
					
						
							|  |  |  |       auto end_of_data = m_->header_length_ + m_->data_size_; | 
					
						
							|  |  |  |       auto length = std::min (size - end_of_data, m_->file_.size () - end_of_data); | 
					
						
							|  |  |  |       if (length > 0) | 
					
						
							|  |  |  |         { | 
					
						
							|  |  |  |           auto position = m_->file_.pos (); | 
					
						
							|  |  |  |           m_->file_.seek (m_->header_length_ + m_->data_size_); | 
					
						
							|  |  |  |           m_->file_.write (QByteArray {int (length), '\0'}); | 
					
						
							|  |  |  |           m_->file_.seek (position); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |       m_->data_size_ = -1; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |   m_->header_dirty_ = true; | 
					
						
							|  |  |  |   return result; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-06-09 23:39:33 +00:00
										 |  |  | bool BWFFile::setPermissions (Permissions permissions) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |   bool success = m_->file_.setPermissions (permissions); | 
					
						
							|  |  |  |   if (!success) setErrorString (m_->file_.errorString ()); | 
					
						
							|  |  |  |   return success; | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2016-01-11 15:00:43 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-06-09 23:39:33 +00:00
										 |  |  | auto BWFFile::error () const -> FileError | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |   return m_->error_ != FileError::NoError ? m_->error_ : m_->file_.error (); | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2016-01-11 15:00:43 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-06-09 23:39:33 +00:00
										 |  |  | bool BWFFile::flush () | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |   bool success = m_->file_.flush (); | 
					
						
							|  |  |  |   if (!success) setErrorString (m_->file_.errorString ()); | 
					
						
							|  |  |  |   return success; | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2016-01-11 15:00:43 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-06-09 23:39:33 +00:00
										 |  |  | int BWFFile::handle () const | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |   int h {m_->file_.handle ()}; | 
					
						
							|  |  |  |   if (h < 0) const_cast<BWFFile *> (this)->setErrorString (m_->file_.errorString ()); | 
					
						
							|  |  |  |   return h; | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2016-01-11 15:00:43 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | uchar * BWFFile::map (qint64 offset, qint64 size, MemoryMapFlags flags) | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2016-06-09 23:39:33 +00:00
										 |  |  |   uchar * address = m_->file_.map (offset + m_->header_length_, size, flags); | 
					
						
							|  |  |  |   if (!address) setErrorString (m_->file_.errorString ()); | 
					
						
							|  |  |  |   return address; | 
					
						
							| 
									
										
										
										
											2016-01-11 15:00:43 +00:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-06-09 23:39:33 +00:00
										 |  |  | bool BWFFile::unmap (uchar * address) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |   bool success = m_->file_.unmap (address); | 
					
						
							|  |  |  |   if (!success) setErrorString (m_->file_.errorString ()); | 
					
						
							|  |  |  |   return success; | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2016-01-11 15:00:43 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-06-09 23:39:33 +00:00
										 |  |  | void BWFFile::unsetError () | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |   m_->error_ = FileError::NoError; | 
					
						
							|  |  |  |   m_->file_.unsetError (); | 
					
						
							|  |  |  | } |