summaryrefslogtreecommitdiffstats
path: root/tderadio3/plugins/recording/recording.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'tderadio3/plugins/recording/recording.cpp')
-rw-r--r--tderadio3/plugins/recording/recording.cpp731
1 files changed, 731 insertions, 0 deletions
diff --git a/tderadio3/plugins/recording/recording.cpp b/tderadio3/plugins/recording/recording.cpp
new file mode 100644
index 0000000..7b685f4
--- /dev/null
+++ b/tderadio3/plugins/recording/recording.cpp
@@ -0,0 +1,731 @@
+/***************************************************************************
+ recording.cpp - description
+ -------------------
+ begin : Mi Aug 27 2003
+ copyright : (C) 2003 by Martin Witte
+ ***************************************************************************/
+
+/***************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ ***************************************************************************/
+
+#include "../../src/include/radiostation.h"
+#include "../../src/include/errorlog-interfaces.h"
+#include "../../src/include/aboutwidget.h"
+#include "../../src/include/fileringbuffer.h"
+#include "../../src/include/utils.h"
+
+#include "recording.h"
+#include "recording-configuration.h"
+#include "soundstreamevent.h"
+#include "recording-monitor.h"
+#include "encoder_mp3.h"
+#include "encoder_ogg.h"
+#include "encoder_pcm.h"
+
+#include <tqevent.h>
+#include <tqapplication.h>
+#include <tqregexp.h>
+
+#include <tdeconfig.h>
+#include <tdeversion.h>
+
+#include <kaboutdata.h>
+
+
+///////////////////////////////////////////////////////////////////////
+//// plugin library functions
+
+PLUGIN_LIBRARY_FUNCTIONS2(
+ Recording, "kradio-recording", i18n("TDERadio Recording Plugin"),
+ RecordingMonitor, i18n("TDERadio Recording Monitor")
+);
+
+///////////////////////////////////////////////////////////////////////
+
+Recording::Recording(const TQString &name)
+ : TQObject(NULL, NULL),
+ PluginBase(name, i18n("TDERadio Recording Plugin")),
+ m_config()
+{
+}
+
+
+Recording::~Recording()
+{
+ TQMapIterator<SoundStreamID, RecordingEncoding*> it = m_EncodingThreads.begin();
+ TQMapIterator<SoundStreamID, RecordingEncoding*> end = m_EncodingThreads.end();
+ for (; it != end; ++it) {
+ sendStopRecording(it.key());
+ }
+}
+
+
+bool Recording::connectI(Interface *i)
+{
+ bool a = IRecCfg::connectI(i);
+ bool b = PluginBase::connectI(i);
+ bool c = ISoundStreamClient::connectI(i);
+ return a || b || c;
+}
+
+
+bool Recording::disconnectI(Interface *i)
+{
+ bool a = IRecCfg::disconnectI(i);
+ bool b = PluginBase::disconnectI(i);
+ bool c = ISoundStreamClient::disconnectI(i);
+ return a || b || c;
+}
+
+
+void Recording::noticeConnectedI (ISoundStreamServer *s, bool pointer_valid)
+{
+ ISoundStreamClient::noticeConnectedI(s, pointer_valid);
+ if (s && pointer_valid) {
+ s->register4_sendStartPlayback(this);
+ s->register4_sendStopPlayback(this);
+ s->register4_sendStartRecording(this);
+ s->register4_sendStartRecordingWithFormat(this);
+ s->register4_notifySoundStreamData(this);
+ s->register4_sendStopRecording(this);
+ s->register4_queryIsRecordingRunning(this);
+ s->register4_querySoundStreamDescription(this);
+ s->register4_querySoundStreamRadioStation(this);
+ s->register4_queryEnumerateSoundStreams(this);
+ s->register4_notifySoundStreamChanged(this);
+ s->register4_notifySoundStreamClosed(this);
+ }
+}
+
+// PluginBase
+
+void Recording::saveState (TDEConfig *c) const
+{
+ c->setGroup(TQString("recording-") + PluginBase::name());
+ m_config.saveConfig(c);
+}
+
+
+void Recording::restoreState (TDEConfig *c)
+{
+ c->setGroup(TQString("recording-") + PluginBase::name());
+ RecordingConfig cfg;
+ cfg.restoreConfig(c);
+ setRecordingConfig(cfg);
+ //notifyRecordingConfigChanged(m_config);
+}
+
+
+ConfigPageInfo Recording::createConfigurationPage()
+{
+ RecordingConfiguration *c = new RecordingConfiguration(NULL);
+ connectI(c);
+ return ConfigPageInfo(c,
+ i18n("Recording"),
+ i18n("Recording"),
+ "kradio_record");
+}
+
+
+AboutPageInfo Recording::createAboutPage()
+{
+/* TDEAboutData aboutData("kradio",
+ NULL,
+ NULL,
+ I18N_NOOP("Recording Monitor for TDERadio"),
+ TDEAboutData::License_GPL,
+ "(c) 2002-2005 Martin Witte",
+ 0,
+ "http://sourceforge.net/projects/kradio",
+ 0);
+ aboutData.addAuthor("Martin Witte", "", "[email protected]");
+
+ return AboutPageInfo(
+ new TDERadioAboutWidget(aboutData, TDERadioAboutWidget::AbtTabbed),
+ i18n("Recording"),
+ i18n("Recording Plugin"),
+ "kradio_record"
+ );*/
+ return AboutPageInfo();
+}
+
+
+// IRecCfg
+
+bool Recording::setEncoderBuffer (size_t BufferSize, size_t BufferCount)
+{
+ if (m_config.m_EncodeBufferSize != BufferSize ||
+ m_config.m_EncodeBufferCount != BufferCount)
+ {
+ m_config.m_EncodeBufferSize = BufferSize;
+ m_config.m_EncodeBufferCount = BufferCount;
+ notifyEncoderBufferChanged(BufferSize, BufferCount);
+ }
+ return true;
+}
+
+bool Recording::setSoundFormat (const SoundFormat &sf)
+{
+ if (m_config.m_SoundFormat != sf) {
+ m_config.m_SoundFormat = sf;
+ notifySoundFormatChanged(sf);
+ }
+ return true;
+}
+
+bool Recording::setMP3Quality (int q)
+{
+ if (m_config.m_mp3Quality != q) {
+ m_config.m_mp3Quality = q;
+ notifyMP3QualityChanged(q);
+ }
+ return true;
+}
+
+bool Recording::setOggQuality (float q)
+{
+ if (m_config.m_oggQuality != q) {
+ m_config.m_oggQuality = q;
+ notifyOggQualityChanged(q);
+ }
+ return true;
+}
+
+bool Recording::setRecordingDirectory(const TQString &dir)
+{
+ if (m_config.m_Directory != dir) {
+ m_config.m_Directory = dir;
+ notifyRecordingDirectoryChanged(dir);
+ }
+ return true;
+}
+
+bool Recording::setOutputFormat (RecordingConfig::OutputFormat of)
+{
+ if (m_config.m_OutputFormat != of) {
+ m_config.m_OutputFormat = of;
+ notifyOutputFormatChanged(of);
+ }
+ return true;
+}
+
+bool Recording::setPreRecording (bool enable, int seconds)
+{
+ if (m_config.m_PreRecordingEnable != enable || m_config.m_PreRecordingSeconds != seconds) {
+ m_config.m_PreRecordingEnable = enable;
+ m_config.m_PreRecordingSeconds = seconds;
+
+ if (enable) {
+ for (TQMapIterator<SoundStreamID,FileRingBuffer*> it = m_PreRecordingBuffers.begin(); it != m_PreRecordingBuffers.end(); ++it) {
+ if (*it != NULL) {
+ delete *it;
+ }
+ *it = new FileRingBuffer(m_config.m_Directory + "/kradio-prerecord-"+TQString::number(it.key().getID()), m_config.m_PreRecordingSeconds * m_config.m_SoundFormat.m_SampleRate * m_config.m_SoundFormat.frameSize());
+ SoundFormat sf = m_config.m_SoundFormat;
+ sendStartCaptureWithFormat(it.key(), sf, sf, false);
+ }
+ }
+ else {
+ for (TQMapIterator<SoundStreamID,FileRingBuffer*> it = m_PreRecordingBuffers.begin(); it != m_PreRecordingBuffers.end(); ++it) {
+ if (*it != NULL) {
+ sendStopCapture(it.key());
+ delete *it;
+ }
+ }
+ m_PreRecordingBuffers.clear();
+ }
+
+ notifyPreRecordingChanged(enable, seconds);
+ }
+ return true;
+}
+
+void Recording::getEncoderBuffer(size_t &BufferSize, size_t &BufferCount) const
+{
+ BufferSize = m_config.m_EncodeBufferSize;
+ BufferCount = m_config.m_EncodeBufferCount;
+}
+
+const SoundFormat &Recording::getSoundFormat () const
+{
+ return m_config.m_SoundFormat;
+}
+
+int Recording::getMP3Quality () const
+{
+ return m_config.m_mp3Quality;
+}
+
+float Recording::getOggQuality () const
+{
+ return m_config.m_oggQuality;
+}
+
+const TQString &Recording::getRecordingDirectory() const
+{
+ return m_config.m_Directory;
+}
+
+RecordingConfig::OutputFormat Recording::getOutputFormat() const
+{
+ return m_config.m_OutputFormat;
+}
+
+bool Recording::getPreRecording(int &seconds) const
+{
+ seconds = m_config.m_PreRecordingSeconds;
+ return m_config.m_PreRecordingEnable;
+}
+
+const RecordingConfig &Recording::getRecordingConfig() const
+{
+ return m_config;
+}
+
+bool Recording::setRecordingConfig(const RecordingConfig &c)
+{
+ setEncoderBuffer (c.m_EncodeBufferSize, c.m_EncodeBufferCount);
+ setSoundFormat (c.m_SoundFormat);
+ setMP3Quality (c.m_mp3Quality);
+ setOggQuality (c.m_oggQuality);
+ setRecordingDirectory(c.m_Directory);
+ setOutputFormat (c.m_OutputFormat);
+ setPreRecording (c.m_PreRecordingEnable, c.m_PreRecordingSeconds);
+
+ m_config = c;
+
+ notifyRecordingConfigChanged(m_config);
+
+ return true;
+}
+
+
+// ISoundStreamClient
+bool Recording::startPlayback(SoundStreamID id)
+{
+ if (m_PreRecordingBuffers.contains(id))
+ delete m_PreRecordingBuffers[id];
+ m_PreRecordingBuffers[id] = NULL;
+ if (m_config.m_PreRecordingEnable) {
+ m_PreRecordingBuffers[id] = new FileRingBuffer(m_config.m_Directory + "/kradio-prerecord-"+TQString::number(id.getID()), m_config.m_PreRecordingSeconds * m_config.m_SoundFormat.m_SampleRate * m_config.m_SoundFormat.frameSize());
+ SoundFormat sf = m_config.m_SoundFormat;
+ sendStartCaptureWithFormat(id, sf, sf, false);
+ }
+ return false;
+}
+
+bool Recording::stopPlayback(SoundStreamID id)
+{
+ if (m_PreRecordingBuffers.contains(id)) {
+ if (m_PreRecordingBuffers[id])
+ delete m_PreRecordingBuffers[id];
+ m_PreRecordingBuffers.remove(id);
+ sendStopCapture(id);
+ }
+ return false;
+}
+
+bool Recording::startRecording(SoundStreamID id)
+{
+
+/* FileRingBuffer *test = new FileRingBuffer("/tmp/ringbuffertest", 2048);
+ char buffer1[1024];
+ char buffer2[1024];
+ char buffer3[1024];
+ for (int i = 0; i < 1024; ++i) {
+ buffer1[i] = 'a';
+ buffer2[i] = 'b';
+ buffer3[i] = 'c';
+ }
+ test->addData(buffer1, 1024);
+ test->addData(buffer2, 1024);
+ test->removeData(1024);
+ test->addData(buffer3, 1024);
+*/
+
+ SoundFormat realFormat = m_config.m_SoundFormat;
+ return sendStartRecordingWithFormat(id, realFormat, realFormat);
+}
+
+bool Recording::startRecordingWithFormat(SoundStreamID id, const SoundFormat &sf, SoundFormat &real_format)
+{
+ if (!sendStartCaptureWithFormat(id, sf, real_format, /* force_format = */ true)) {
+ logError(i18n("start capture not handled"));
+ return false;
+ }
+
+ RecordingConfig cfg = m_config;
+ cfg.m_SoundFormat = real_format;
+
+ logInfo(i18n("Recording starting"));
+ if (!startEncoder(id, cfg)) {
+ logError(i18n("starting encoding thread failed"));
+ sendStopCapture(id);
+ return false;
+ }
+
+ return true;
+}
+
+
+bool Recording::stopRecording(SoundStreamID id)
+{
+ if (m_EncodingThreads.contains(id)) {
+ sendStopCapture(id);
+ if (m_config.m_PreRecordingEnable) {
+ if (!m_PreRecordingBuffers.contains(id)) {
+ if (m_PreRecordingBuffers[id] != NULL) {
+ delete m_PreRecordingBuffers[id];
+ }
+ bool b = false;
+ queryIsPlaybackRunning(id, b);
+ if (b) {
+ m_PreRecordingBuffers[id] = new FileRingBuffer(m_config.m_Directory + "/kradio-prerecord-"+TQString::number(id.getID()), m_config.m_PreRecordingSeconds * m_config.m_SoundFormat.m_SampleRate * m_config.m_SoundFormat.frameSize());
+ } else {
+ m_PreRecordingBuffers[id] = NULL;
+ }
+ }
+ }
+ stopEncoder(id);
+ return true;
+ }
+ return false;
+}
+
+
+
+bool Recording::noticeSoundStreamData(SoundStreamID id,
+ const SoundFormat &/*sf*/, const char *data, size_t size, size_t &consumed_size,
+ const SoundMetaData &md
+)
+{
+ if (m_PreRecordingBuffers.contains(id) && m_PreRecordingBuffers[id] != NULL) {
+
+ FileRingBuffer &fbuf = *m_PreRecordingBuffers[id];
+ if (fbuf.getFreeSize() < size) {
+ fbuf.removeData(size - fbuf.getFreeSize());
+ }
+ size_t n = fbuf.addData(data, size);
+ consumed_size = (consumed_size == SIZE_T_DONT_CARE) ? n : min(consumed_size, n);
+// if (n != size) {
+// logDebug("recording packet: was not written completely to tmp buf");
+// }
+
+// //BEGIN DEBUG
+// char tmp[4096];
+// for (unsigned int i = 0; i < sizeof(tmp); ++i) { tmp[i] = 0; }
+// if (fbuf.getFreeSize() < sizeof(tmp)) {
+// fbuf.removeData(sizeof(tmp) - fbuf.getFreeSize());
+// }
+// fbuf.addData((char*)tmp, sizeof(tmp));
+// //END DEBUG
+
+ if (m_EncodingThreads.contains(id)) {
+
+ //logDebug("recording packet: " + TQString::number(size));
+
+ RecordingEncoding *thread = m_EncodingThreads[id];
+
+ //logDebug("noticeSoundStreamData thread = " + TQString::number((long long)thread, 16));
+
+ size_t remSize = fbuf.getFillSize();
+
+ while (remSize > 0) {
+ size_t bufferSize = remSize;
+ char *buf = thread->lockInputBuffer(bufferSize);
+ if (!buf) {
+ // Encoder buffer is full and bigger than remaining data
+ break;
+ }
+ if (bufferSize > remSize) {
+ bufferSize = remSize;
+ }
+ if (fbuf.takeData(buf, bufferSize) != bufferSize) {
+ logError(i18n("could not read suffient data"));
+ }
+
+ thread->unlockInputBuffer(bufferSize, md);
+ remSize -= bufferSize;
+ }
+
+ if (remSize == 0) {
+ delete m_PreRecordingBuffers[id];
+ m_PreRecordingBuffers.remove(id);
+ }
+ }
+
+ return true;
+ }
+
+ else if (m_EncodingThreads.contains(id)) {
+
+ //logDebug("recording packet: " + TQString::number(size));
+
+ RecordingEncoding *thread = m_EncodingThreads[id];
+
+ //logDebug("noticeSoundStreamData thread = " + TQString::number((long long)thread, 16));
+
+ size_t remSize = size;
+ const char *remData = data;
+
+ while (remSize > 0) {
+ size_t bufferSize = remSize;
+ char *buf = thread->lockInputBuffer(bufferSize);
+ if (!buf) {
+ logWarning(i18n("Encoder input buffer overflow (buffer configuration problem?). Skipped %1 input bytes").arg(TQString::number(remSize)));
+ break;
+ }
+ if (bufferSize > remSize) {
+ bufferSize = remSize;
+ }
+ memcpy(buf, remData, bufferSize);
+
+ thread->unlockInputBuffer(bufferSize, md);
+ remSize -= bufferSize;
+ remData += bufferSize;
+ }
+ consumed_size = (consumed_size == SIZE_T_DONT_CARE) ? size - remSize : min(consumed_size, size - remSize);
+
+ return true;
+ }
+ return false;
+}
+
+
+
+
+bool Recording::startEncoder(SoundStreamID ssid, const RecordingConfig &cfg)
+{
+ if (m_EncodingThreads.contains(ssid))
+ return false;
+
+ SoundStreamID encID = createNewSoundStream(ssid, false);
+ m_RawStreams2EncodedStreams[ssid] = encID;
+ m_EncodedStreams2RawStreams[encID] = ssid;
+
+ TQString ext = ".wav";
+ switch (m_config.m_OutputFormat) {
+ case RecordingConfig::outputWAV: ext = ".wav"; break;
+ case RecordingConfig::outputAIFF: ext = ".aiff"; break;
+ case RecordingConfig::outputAU: ext = ".au"; break;
+#ifdef HAVE_LAME
+ case RecordingConfig::outputMP3: ext = ".mp3"; break;
+#endif
+#ifdef HAVE_LAME
+ case RecordingConfig::outputOGG: ext = ".ogg"; break;
+#endif
+ case RecordingConfig::outputRAW: ext = ".raw"; break;
+ default: ext = ".wav"; break;
+ }
+ const RadioStation *rs = NULL;
+ querySoundStreamRadioStation(ssid, rs);
+ TQString station = rs ? rs->name() + "-" : "";
+ station.replace(TQRegExp("[/*?]"), "_");
+
+ TQDate date = TQDate::currentDate();
+ TQTime time = TQTime::currentTime();
+ TQString sdate;
+
+ sdate.sprintf("%d.%d.%d.%d.%d",date.year(),date.month(),date.day(),time.hour(),time.minute());
+
+ TQString output = m_config.m_Directory
+ + "/kradio-recording-"
+ + station
+ + sdate
+ + ext;
+
+ logInfo(i18n("Recording::outputFile: ") + output);
+
+ RecordingEncoding *thread = NULL;
+ switch (m_config.m_OutputFormat) {
+#ifdef HAVE_LAME
+ case RecordingConfig::outputMP3:
+ thread = new RecordingEncodingMP3(this, ssid, cfg, rs, output);
+ break;
+#endif
+#ifdef HAVE_OGG
+ case RecordingConfig::outputOGG:
+ thread = new RecordingEncodingOgg(this, ssid, cfg, rs, output);
+ break;
+#endif
+ default:
+ thread = new RecordingEncodingPCM(this, ssid, cfg, rs, output);
+ }
+
+ //m_encodingThread->openOutput(output, rs);
+
+ if (thread->error()) {
+ //m_context.setError();
+ logError(thread->errorString());
+ } else {
+ thread->start();
+ }
+ // store thread even if it has indicated an error
+ m_EncodingThreads[ssid] = thread;
+
+ //logDebug("startEncoder thread = " + TQString::number((long long)thread, 16));
+
+ notifySoundStreamCreated(encID);
+ return !thread->error();
+}
+
+
+void Recording::stopEncoder(SoundStreamID id)
+{
+ if (m_EncodingThreads.contains(id)) {
+
+ RecordingEncoding *thread = m_EncodingThreads[id];
+
+ thread->setDone();
+
+ //logDebug("stopEncoder thread = " + TQString::number((long long)thread, 16));
+ //logDebug("stopEncoder thread error = " + TQString::number(thread->error(), 16));
+
+ // FIXME: set a timer and do waiting "in background"
+ if (!thread->wait(5000)) {
+ //m_context.setError();
+ logError(i18n("The encoding thread did not finish. It will be killed now."));
+ thread->terminate();
+ thread->wait();
+ } else {
+ if (thread->error()) {
+ //m_context.setError();
+ logError(thread->errorString());
+ } else {
+ //TQ_UINT64 size = thread->encodedSize();
+ //m_context.setEncodedSize(low, high);
+ //notifyRecordingContextChanged(m_context);
+ }
+ }
+ delete thread;
+ m_EncodingThreads.remove(id);
+ SoundStreamID encID = m_RawStreams2EncodedStreams[id];
+ m_EncodedStreams2RawStreams.remove(encID);
+ m_RawStreams2EncodedStreams.remove(id);
+ sendStopPlayback(encID);
+ closeSoundStream(encID);
+ logInfo(i18n("Recording stopped"));
+ }
+}
+
+
+bool Recording::event(TQEvent *_e)
+{
+ if (SoundStreamEvent::isSoundStreamEvent(_e)) {
+ SoundStreamEvent *e = static_cast<SoundStreamEvent*>(_e);
+ SoundStreamID id = e->getSoundStreamID();
+
+ if (m_EncodingThreads.contains(id)) {
+
+ RecordingEncoding *thread = m_EncodingThreads[id];
+
+ //logDebug("Recording::event: thread = " + TQString::number((long long)thread, 16));
+
+ if (thread->error()) {
+ logError(thread->errorString());
+ //m_context.setError();
+ stopEncoder(id);
+ } else {
+ //TQ_UINT64 size = thread->encodedSize();
+ //m_context.setEncodedSize(low, high);
+ //notifyRecordingContextChanged(m_context);
+ if (e->type() == EncodingTerminated) {
+ stopEncoder(id);
+ } else if (e->type() == EncodingStep) {
+ SoundStreamEncodingStepEvent *step = static_cast<SoundStreamEncodingStepEvent*>(e);
+ size_t consumed_size = SIZE_T_DONT_CARE;
+ notifySoundStreamData(m_RawStreams2EncodedStreams[id], thread->config().m_SoundFormat,
+ step->data(), step->size(), consumed_size, step->metaData());
+ if (consumed_size != SIZE_T_DONT_CARE && consumed_size < step->size()) {
+ logError(i18n("Recording::notifySoundStreamData(encoded data): Receivers skipped %1 Bytes").arg(step->size() - consumed_size));
+ }
+ }
+ }
+ }
+ return true;
+ } else {
+ return TQObject::event(_e);
+ }
+}
+
+
+bool Recording::getSoundStreamDescription(SoundStreamID id, TQString &descr) const
+{
+ if (m_EncodedStreams2RawStreams.contains(id)) {
+ if (querySoundStreamDescription(m_EncodedStreams2RawStreams[id], descr)) {
+ descr = name() + " - " + descr;
+ return true;
+ }
+ }
+ return false;
+}
+
+
+bool Recording::getSoundStreamRadioStation(SoundStreamID id, const RadioStation *&rs) const
+{
+ if (m_EncodedStreams2RawStreams.contains(id)) {
+ if (querySoundStreamRadioStation(m_EncodedStreams2RawStreams[id], rs)) {
+ return true;
+ }
+ }
+ return false;
+}
+
+
+bool Recording::enumerateSoundStreams(TQMap<TQString, SoundStreamID> &list) const
+{
+ TQMapConstIterator<SoundStreamID,SoundStreamID> end = m_RawStreams2EncodedStreams.end();
+ for (TQMapConstIterator<SoundStreamID,SoundStreamID> it = m_RawStreams2EncodedStreams.begin(); it != end; ++it) {
+ TQString tmp = TQString();
+ getSoundStreamDescription(*it, tmp);
+ list[tmp] = *it;
+ }
+ return m_RawStreams2EncodedStreams.count() > 0;
+}
+
+
+bool Recording::noticeSoundStreamChanged(SoundStreamID id)
+{
+ if (m_RawStreams2EncodedStreams.contains(id)) {
+ notifySoundStreamChanged(m_RawStreams2EncodedStreams[id]);
+ return true;
+ }
+ return false;
+}
+
+
+bool Recording::isRecordingRunning(SoundStreamID id, bool &b, SoundFormat &sf) const
+{
+ if (m_EncodingThreads.contains(id)) {
+ b = m_EncodingThreads[id]->running();
+ sf = getSoundFormat();
+ return true;
+ }
+ return false;
+}
+
+
+bool Recording::noticeSoundStreamClosed(SoundStreamID id)
+{
+ if (m_PreRecordingBuffers.contains(id)) {
+ if (m_PreRecordingBuffers[id])
+ delete m_PreRecordingBuffers[id];
+ m_PreRecordingBuffers.remove(id);
+ }
+
+ if (m_EncodingThreads.contains(id)) {
+ sendStopRecording(id);
+ return true;
+ }
+ return false;
+}
+
+
+#include "recording.moc"