kwave  18.07.70
MimeData.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  KwaveMimeData.cpp - mime data container for Kwave's audio data
3  -------------------
4  begin : Oct 04 2008
5  copyright : (C) 2008 by Thomas Eschenbacher
6  email : Thomas Eschenbacher <thomas.eschenbacher@gmx.de>
7  ***************************************************************************/
8 
9 /***************************************************************************
10  * *
11  * This program is free software; you can redistribute it and/or modify *
12  * it under the terms of the GNU General Public License as published by *
13  * the Free Software Foundation; either version 2 of the License, or *
14  * (at your option) any later version. *
15  * *
16  ***************************************************************************/
17 
18 #include "config.h"
19 
20 #include <limits>
21 #include <new>
22 
23 #include <QApplication>
24 #include <QBuffer>
25 #include <QMutableListIterator>
26 #include <QVariant>
27 #include <QWidget>
28 
29 #include "libkwave/CodecManager.h"
30 #include "libkwave/Compression.h"
31 #include "libkwave/Connect.h"
32 #include "libkwave/Decoder.h"
33 #include "libkwave/Encoder.h"
34 #include "libkwave/MimeData.h"
38 #include "libkwave/Sample.h"
39 #include "libkwave/SampleReader.h"
40 #include "libkwave/Signal.h"
41 #include "libkwave/SignalManager.h"
42 #include "libkwave/String.h"
43 #include "libkwave/Utils.h"
44 #include "libkwave/Writer.h"
45 
48 
49 // RFC 2361:
50 #define WAVE_FORMAT_PCM "audio/vnd.wave" // ; codec=001"
51 
53 #define BUFFER_BLOCK_SIZE (4 * 1024 * 1024) /* 4 MB */
54 
55 //***************************************************************************
57  :QIODevice(), m_block(0), m_size(0), m_data()
58 {
59 }
60 
61 //***************************************************************************
63 {
64  close();
65 }
66 
67 //***************************************************************************
68 qint64 Kwave::MimeData::Buffer::readData(char *data, qint64 maxlen)
69 {
70  if (atEnd() || (pos() >= size())) return -1;
71  if (pos() + maxlen > size())
72  maxlen = size() - pos();
73 
75  return mem.readFrom(
76  m_block,
77  Kwave::toUint(pos()),
78  data,
79  Kwave::toUint(maxlen)
80  );
81 }
82 
83 //***************************************************************************
84 qint64 Kwave::MimeData::Buffer::writeData(const char *data, qint64 len)
85 {
87  quint64 new_size = pos() + len;
88 
89  // clip the mime data buffer at the "unsigned int" border
90  if (new_size > std::numeric_limits<unsigned int>::max())
91  return -1;
92 
93  // round up the block size if it can no longer be considered to be a
94  // small block (~ half of block size), avoid wasting too much memory
95  // if the needed size is very small.
96  if (new_size > (BUFFER_BLOCK_SIZE / 2))
97  new_size = Kwave::round_up<qint64>(new_size, BUFFER_BLOCK_SIZE);
98 
99  if (!m_block) {
100  // first call: allocate a new memory object
101  m_block = mem.allocate(static_cast<size_t>(new_size));
102  if (!m_block) return -1; // allocation failed
103  }
104 
105  if ((pos() + len) > static_cast<qint64>(mem.sizeOf(m_block))) {
106  if (!mem.resize(m_block, static_cast<size_t>(new_size)))
107  return -1; // resize failed
108  }
109 
110  // write to the memory block (may be physical or swap file)
111  qint64 written = mem.writeTo(
112  m_block,
113  static_cast<unsigned int>(pos()),
114  data,
115  static_cast<unsigned int>(len)
116  );
117  if (written < 0)
118  return -1; // write failed: disk full?
119 
120  if (pos() + written > m_size)
121  m_size = pos() + written ; // push the "m_size"
122 
123  return written; // write operation was successful
124 }
125 
126 //***************************************************************************
128 {
129  // reset our QByteArray
130  m_data.setRawData(Q_NULLPTR, 0);
131  m_data.clear();
132 
134  const char *raw = (m_block) ?
135  static_cast<const char *>(mem.map(m_block)) : Q_NULLPTR;
136  if (!raw) {
137  // mapping failed: free the block here to avoid trouble
138  // in close()
139  mem.free(m_block);
140  m_block = 0;
141  qWarning("Kwave::MimeData::Buffer::mapToByteArray() failed");
142  return false; // mmap failed
143  }
144 
145  // attach the mapped memory to our QByteArray
146  const unsigned int len = Kwave::toUint(m_size);
147 // qDebug("Kwave::MimeData::Buffer::mapToByteArray() - %p [%u]", raw, len);
148  m_data.setRawData(raw, len);
149  return true;
150 }
151 
152 //***************************************************************************
154 {
155  QIODevice::close();
156 
157  // reset the byte array and it's connection to the block of memory
158  m_data.setRawData(Q_NULLPTR, 0);
159  m_data.clear();
160 
161  // unmap and discard the mapped memory
162  if (m_block) {
164  mem.unmap(m_block);
165  mem.free(m_block);
166  m_block = 0;
167  }
168  m_size = 0;
169 }
170 
171 //***************************************************************************
172 //***************************************************************************
174  :QMimeData(), m_buffer()
175 {
176 }
177 
178 //***************************************************************************
180 {
181 }
182 
183 //***************************************************************************
184 bool Kwave::MimeData::encode(QWidget *widget,
186  const Kwave::MetaDataList &meta_data)
187 {
188  // use our default encoder
190  Q_ASSERT(encoder);
191  if (!encoder) return false;
192 
193  Q_ASSERT(src.tracks());
194  if (!src.tracks()) return false;
195 
196  // set hourglass cursor
197  QApplication::setOverrideCursor(QCursor(Qt::WaitCursor));
198 
199  sample_index_t first = src.first();
200  sample_index_t last = src.last();
201  Kwave::MetaDataList new_meta_data = meta_data.selectByRange(first, last);
202 
203  // move all meta data left, to start at the beginning of the selection
204  new_meta_data.shiftLeft(first, first, QList<unsigned int>());
205 
206  // fix the length information in the new file info
207  // and change to uncompressed mode
208  Kwave::FileInfo info(meta_data);
210  info.setLength(last - first + 1);
211  info.setTracks(src.tracks());
212  new_meta_data.replace(Kwave::MetaDataList(info));
213 
214  // encode into the buffer
215  m_buffer.close(); // discard old stuff
216  encoder->encode(widget, src, m_buffer, new_meta_data);
217 
218  delete encoder;
219 
220  // set the mime data into this mime data container
221  bool succeeded = m_buffer.mapToByteArray();
222  if (succeeded) {
223  // mmap succeeded
224  setData(_(WAVE_FORMAT_PCM), m_buffer.byteArray());
225  } else {
226  // failed to map memory
227  m_buffer.close();
228  }
229 
230  // remove hourglass
231  QApplication::restoreOverrideCursor();
232 
233  return succeeded;
234 }
235 
236 //***************************************************************************
237 sample_index_t Kwave::MimeData::decode(QWidget *widget, const QMimeData *e,
239  sample_index_t pos)
240 {
241  // decode, use the first format that matches
242  sample_index_t decoded_length = 0;
243  unsigned int decoded_tracks = 0;
244 
245  // try to find a suitable decoder
246  foreach (const QString &format, e->formats()) {
247  // skip all non-supported formats
248  if (!Kwave::CodecManager::canDecode(format)) continue;
249 
250  Kwave::Decoder *decoder = Kwave::CodecManager::decoder(format);
251  Q_ASSERT(decoder);
252  if (!decoder) return 0;
253 
254  QByteArray raw_data = e->data(format);
255  QBuffer src(&raw_data);
256 
257  // open the mime source and get header information
258  bool ok = decoder->open(widget, src);
259  if (!ok) {
260  delete decoder;
261  continue;
262  }
263 
264  decoded_length = Kwave::FileInfo(decoder->metaData()).length();
265  decoded_tracks = Kwave::FileInfo(decoder->metaData()).tracks();
266  Q_ASSERT(decoded_length);
267  Q_ASSERT(decoded_tracks);
268  if (!decoded_length || !decoded_tracks) {
269  delete decoder;
270  continue;
271  }
272 
273  // get sample rates of source and destination
274  double src_rate = Kwave::FileInfo(decoder->metaData()).rate();
275  double dst_rate = sig.rate();
276 
277  // if the sample rate has to be converted, adjust the length
278  // right border
279  if (!qFuzzyCompare(src_rate, dst_rate) && (dst_rate > 1) && sig.tracks())
280  decoded_length *= (dst_rate / src_rate);
281 
282  sample_index_t left = pos;
283  sample_index_t right = left + decoded_length - 1;
284  QList<unsigned int> tracks = sig.selectedTracks();
285  if (tracks.isEmpty()) tracks = sig.allTracks();
286 
287  // special case: destination is currently empty
288  if (!sig.tracks()) {
289  // encode into an empty window -> create tracks
290  qDebug("Kwave::MimeData::decode(...) -> new signal");
291  dst_rate = src_rate;
292  sig.newSignal(0,
293  src_rate,
294  Kwave::FileInfo(decoder->metaData()).bits(),
295  decoded_tracks);
296  ok = (sig.tracks() == decoded_tracks);
297  if (!ok) {
298  delete decoder;
299  continue;
300  }
301  }
302  const unsigned int dst_tracks = sig.selectedTracks().count();
303 
304  // create the final sink
306  Kwave::Insert, left, right);
307 
308  // if the track count does not match, then we need a channel mixer
309  Q_ASSERT(ok);
310  Kwave::ChannelMixer *mixer = Q_NULLPTR;
311  if (ok && (decoded_tracks != dst_tracks)) {
312  qDebug("Kwave::MimeData::decode(...) -> mixing channels: %u -> %u",
313  decoded_tracks, dst_tracks);
314  mixer = new(std::nothrow)
315  Kwave::ChannelMixer(decoded_tracks, dst_tracks);
316  Q_ASSERT(mixer);
317  ok &= (mixer) && mixer->init();
318  Q_ASSERT(ok);
319  }
320  Q_ASSERT(ok);
321 
322  // if the sample rates do not match, then we need a rate converter
323  Kwave::StreamObject *rate_converter = Q_NULLPTR;
324  if (ok && !qFuzzyCompare(src_rate, dst_rate)) {
325  // create a sample rate converter
326  qDebug("Kwave::MimeData::decode(...) -> rate conversion: "\
327  "%0.1f -> %0.1f", src_rate, dst_rate);
328  rate_converter = new(std::nothrow)
330  dst_tracks, widget);
331  Q_ASSERT(rate_converter);
332  if (rate_converter)
333  rate_converter->setAttribute(SLOT(setRatio(QVariant)),
334  QVariant(dst_rate / src_rate));
335  else
336  ok = false;
337  }
338  Q_ASSERT(ok);
339 
340  // set hourglass cursor
341  QApplication::setOverrideCursor(QCursor(Qt::WaitCursor));
342 
343  if (ok && (rate_converter || mixer)) {
344  // pass all data through a filter chain
345  Kwave::MultiStreamWriter adapter(decoded_tracks);
346 
347  // pass the data through a sample rate converter
348  // decoder -> adapter -> [mixer] -> [converter] -> dst
349 
350  Kwave::StreamObject *last_output = &adapter;
351 
352  if (ok && mixer) {
353  // connect the channel mixer
354  ok = Kwave::connect(
355  *last_output, SIGNAL(output(Kwave::SampleArray)),
356  *mixer, SLOT(input(Kwave::SampleArray))
357  );
358  last_output = mixer;
359  }
360 
361  if (ok && rate_converter) {
362  // connect the rate converter
363  ok = Kwave::connect(
364  *last_output, SIGNAL(output(Kwave::SampleArray)),
365  *rate_converter, SLOT(input(Kwave::SampleArray))
366  );
367  last_output = rate_converter;
368  }
369 
370  // connect the sink
371  if (ok) {
372  ok = Kwave::connect(
373  *last_output, SIGNAL(output(Kwave::SampleArray)),
374  dst, SLOT(input(Kwave::SampleArray))
375  );
376  }
377 
378  // this also starts the conversion automatically
379  if (ok)
380  ok = decoder->decode(widget, adapter);
381 
382  // flush all samples that are still in the adapter
383  adapter.flush();
384 
385  } else if (ok) {
386  // decode directly without any filter
387  ok = decoder->decode(widget, dst);
388  }
389 
390  dst.flush();
391 
392  // clean up the filter chain
393  if (mixer) delete mixer;
394  if (rate_converter) delete rate_converter;
395 
396  // remove hourglass
397  QApplication::restoreOverrideCursor();
398 
399  // failed :-(
400  Q_ASSERT(ok);
401  if (!ok) {
402  delete decoder;
403  decoded_length = 0;
404  continue;
405  }
406 
407  // take care of the meta data, shift all it by "left" and
408  // add it to the signal
409  Kwave::MetaDataList meta_data = decoder->metaData();
410 
411  // adjust meta data position in case of different sample rate
412  if (!qFuzzyCompare(src_rate, dst_rate))
413  meta_data.scalePositions(dst_rate / src_rate, tracks);
414 
415  meta_data.shiftRight(0, left, tracks);
416 
417  // remove the file info, this must not be handled here, otherwise
418  // this would overwrite the file info of the destination
419  meta_data.remove(meta_data.selectByType(
421 
422  // add the remaining meta data (e.g. labels etc)
423  sig.metaData().add(meta_data);
424 
425  delete decoder;
426  break;
427  }
428 
429 // qDebug("Kwave::MimeData::decode -> decoded_length=%u", decoded_length);
430  return decoded_length;
431 }
432 
433 //***************************************************************************
435 {
436  m_buffer.close();
437 }
438 
439 //***************************************************************************
440 //***************************************************************************
virtual void clear()
Definition: MimeData.cpp:434
virtual MetaDataList selectByRange(sample_index_t first, sample_index_t last) const
#define BUFFER_BLOCK_SIZE
Definition: MimeData.cpp:53
virtual sample_index_t first() const
int readFrom(Kwave::Handle handle, unsigned int offset, void *buffer, unsigned int length) Q_DECL_EXPORT
bool resize(Kwave::Handle handle, size_t size) Q_DECL_EXPORT
size_t sizeOf(Kwave::Handle handle) Q_DECL_EXPORT
Kwave::Handle m_block
Definition: MimeData.h:141
static MemoryManager & instance() Q_DECL_EXPORT
Kwave::MimeData::Buffer m_buffer
Definition: MimeData.h:153
virtual void shiftRight(sample_index_t offset, sample_index_t shift, const QList< unsigned int > &tracks)
virtual unsigned int tracks() const Q_DECL_OVERRIDE
Kwave::MetaDataList & metaData()
virtual void remove(const MetaData &metadata)
virtual qint64 size() const Q_DECL_OVERRIDE
Definition: MimeData.h:99
virtual void scalePositions(double scale, const QList< unsigned int > &tracks)
virtual void close() Q_DECL_OVERRIDE
Definition: MimeData.cpp:153
void newSignal(sample_index_t samples, double rate, unsigned int bits, unsigned int tracks)
unsigned int tracks()
quint64 sample_index_t
Definition: Sample.h:28
virtual bool open(QWidget *widget, QIODevice &source)=0
virtual void add(const MetaData &metadata)
bool connect(Kwave::StreamObject &source, const char *output, Kwave::StreamObject &sink, const char *input)
Definition: Connect.cpp:48
void set(FileProperty key, const QVariant &value)
Definition: FileInfo.cpp:363
virtual MetaDataList selectByType(const QString &type) const
void free(Kwave::Handle &handle) Q_DECL_EXPORT
virtual bool init()
virtual qint64 writeData(const char *data, qint64 len) Q_DECL_OVERRIDE
Definition: MimeData.cpp:84
virtual void flush()
virtual sample_index_t last() const
static sample_index_t decode(QWidget *widget, const QMimeData *e, Kwave::SignalManager &sig, sample_index_t pos)
Definition: MimeData.cpp:237
virtual qint64 readData(char *data, qint64 maxlen) Q_DECL_OVERRIDE
Definition: MimeData.cpp:68
void setLength(sample_index_t length)
Definition: FileInfo.cpp:409
double rate() const
void setTracks(unsigned int tracks)
Definition: FileInfo.cpp:454
virtual ~Buffer() Q_DECL_OVERRIDE
Definition: MimeData.cpp:62
virtual void replace(const MetaDataList &list)
virtual Kwave::MetaDataList & metaData()
Definition: Decoder.h:78
const QList< unsigned int > allTracks()
virtual bool encode(QWidget *widget, Kwave::MultiTrackReader &src, QIODevice &dst, const Kwave::MetaDataList &meta_data)=0
static QString metaDataType()
Definition: FileInfo.h:154
virtual bool decode(QWidget *widget, Kwave::MultiWriter &dst)=0
virtual ~MimeData() Q_DECL_OVERRIDE
Definition: MimeData.cpp:179
virtual bool encode(QWidget *widget, Kwave::MultiTrackReader &src, const Kwave::MetaDataList &meta_data)
Definition: MimeData.cpp:184
static Kwave::Decoder * decoder(const QString &mimetype_name)
static Kwave::Encoder * encoder(const QString &mimetype_name)
#define _(m)
Definition: memcpy.c:66
void unmap(Kwave::Handle handle) Q_DECL_EXPORT
void * map(Kwave::Handle handle) Q_DECL_EXPORT
int writeTo(Kwave::Handle handle, unsigned int offset, const void *buffer, unsigned int length) Q_DECL_EXPORT
const QList< unsigned int > selectedTracks()
Kwave::Handle allocate(size_t size) Q_DECL_EXPORT
unsigned int toUint(T x)
Definition: Utils.h:109
virtual void shiftLeft(sample_index_t offset, sample_index_t shift, const QList< unsigned int > &tracks)
const QByteArray & byteArray() const
Definition: MimeData.h:109
static bool canDecode(const QString &mimetype_name)
void setAttribute(const char *attribute, const QVariant &value)