diff options
Diffstat (limited to 'tderadio3/plugins/recording')
38 files changed, 5517 insertions, 0 deletions
diff --git a/tderadio3/plugins/recording/Makefile.am b/tderadio3/plugins/recording/Makefile.am new file mode 100644 index 0000000..1cbd6aa --- /dev/null +++ b/tderadio3/plugins/recording/Makefile.am @@ -0,0 +1,22 @@ +SUBDIRS = po icons . + +INCLUDES = $(all_includes) +METASOURCES = AUTO + +libkradio_LTLIBRARIES = librecording.la +librecording_la_SOURCES = recording-configuration.cpp \ + recording-configuration-ui.ui recording.cpp recording-config.cpp reccfg_interfaces.cpp encoder.cpp \ + recording-datamonitor.cpp recording-monitor.cpp encoder_mp3.cpp encoder_ogg.cpp encoder_pcm.cpp +librecording_la_LDFLAGS = -module -avoid-version $(KDE_RPATH) $(all_libraries) +librecording_la_LIBADD = $(LIB_OGG) $(LIB_LAME) + +noinst_HEADERS = recording-configuration.h recording.h recording-config.h \ + reccfg_interfaces.h encoder.h soundstreamevent.h recording-datamonitor.h \ + recording-monitor.h encoder_mp3.h encoder_ogg.h encoder_pcm.h + +#messages: rc.cpp +# $(XGETTEXT) *.cpp *.h -o po/kradio-recording.pot + +messages: rc.cpp + $(EXTRACTRC) *.rc *.ui >> rc.cpp + $(XGETTEXT) rc.cpp *.h *.cpp -o po/kradio-recording.pot diff --git a/tderadio3/plugins/recording/encoder.cpp b/tderadio3/plugins/recording/encoder.cpp new file mode 100644 index 0000000..c1e27c3 --- /dev/null +++ b/tderadio3/plugins/recording/encoder.cpp @@ -0,0 +1,172 @@ +/*************************************************************************** + encoder.cpp - description + ------------------- + begin : Thu May 05 2005 + copyright : (C) 2005 by Martin Witte + email : witte@kawo1.rwth-aachen.de + ***************************************************************************/ + +/*************************************************************************** + * * + * 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 "recording.h" +#include "recording-configuration.h" +#include "soundstreamevent.h" + +#include <tqsocketnotifier.h> +#include <tqevent.h> +#include <tqapplication.h> +#include <tqregexp.h> + +#include <tdeconfig.h> +#include <tdeversion.h> +#include <klocale.h> + +RecordingEncoding::RecordingEncoding(TQObject *parent, SoundStreamID ssid, + const RecordingConfig &cfg, const RadioStation *rs, + const TQString &filename) + : + m_parent(parent), + m_config(cfg), + m_RadioStation(rs ? rs->copy() : NULL), + m_SoundStreamID(ssid), + m_error(false), + m_errorString(TQString()), + m_done(false), + m_InputBuffers(m_config.m_EncodeBufferCount < 3 ? 3 : m_config.m_EncodeBufferCount, + m_config.m_EncodeBufferSize < 4096 ? 4096 : m_config.m_EncodeBufferSize), + m_buffersMetaData(NULL), + m_encodedSize(0), + m_InputStartTime(0), + m_InputStartPosition(0), + m_outputURL(filename) +{ + + if (m_config.m_EncodeBufferCount < 3) + m_config.m_EncodeBufferCount = 3; + if (m_config.m_EncodeBufferSize < 4096) + m_config.m_EncodeBufferSize = 4096; + + m_buffersMetaData = new TQPtrList<BufferSoundMetaData> *[m_config.m_EncodeBufferCount]; + for (size_t i = 0; i < m_config.m_EncodeBufferCount; ++i) { + m_buffersMetaData [i] = new TQPtrList<BufferSoundMetaData>; + m_buffersMetaData [i]->setAutoDelete(true); + } +} + + +RecordingEncoding::~RecordingEncoding() +{ + for (size_t i = 0; i < m_config.m_EncodeBufferCount; ++i) { + delete m_buffersMetaData[i]; + } + delete m_buffersMetaData; + delete m_RadioStation; +} + + +char *RecordingEncoding::lockInputBuffer(size_t &bufferSize) +{ + if (m_done || m_error) + return NULL; + char * retval = m_InputBuffers.lockWriteBuffer(bufferSize); + + m_error |= m_InputBuffers.hasError(); + m_errorString += m_InputBuffers.getErrorString(); + m_InputBuffers.resetError(); + + return retval; +} + + +void RecordingEncoding::unlockInputBuffer(size_t bufferSize, const SoundMetaData &md) +{ + if (m_done) + return; + size_t bufidx = m_InputBuffers.getCurrentWriteBufferIdx(); + size_t buffill = m_InputBuffers.getWriteBufferFill(); + m_InputBuffers.unlockWriteBuffer(bufferSize); + + if (!m_InputBuffers.hasError()) { + if (!m_InputStartTime) { + m_InputStartTime = md.absoluteTimestamp(); + m_InputStartPosition = md.position(); + } + BufferSoundMetaData *bmd = new BufferSoundMetaData( + md.position() - m_InputStartPosition, + md.absoluteTimestamp() - m_InputStartTime, + md.absoluteTimestamp(), + md.url(), + buffill); + m_buffersMetaData[bufidx]->append(bmd); + } else { + m_error = true; + m_errorString += m_InputBuffers.getErrorString(); + m_InputBuffers.resetError(); + } +} + + +void RecordingEncoding::setDone() +{ + m_done = true; + m_InputBuffers.unlockAllWriteBuffers(); +} + + + +void RecordingEncoding::run() +{ + BufferSoundMetaData last_md; + + while (!m_error) { + char *buffer = NULL; + size_t buffer_fill = 0; + if (!m_done) { + buffer = m_InputBuffers.wait4ReadBuffer(buffer_fill); + } + + if (!buffer_fill) { + if (m_done) + break; + else + continue; + } + + char *export_buffer = NULL; + size_t export_buffer_size = 0; + + TQ_UINT64 old_pos = m_encodedSize; + + encode(buffer, buffer_fill, export_buffer, export_buffer_size); + + SoundStreamEncodingStepEvent *step_event = NULL; + + if (!m_error) { + last_md = *m_buffersMetaData[m_InputBuffers.getCurrentReadBufferIdx()]->first(); + SoundMetaData md(old_pos, last_md.relativeTimestamp(), last_md.absoluteTimestamp(), m_outputURL); + step_event = new SoundStreamEncodingStepEvent(m_SoundStreamID, export_buffer, export_buffer_size, md); + } + + if (step_event) + TQApplication::postEvent(m_parent, step_event); + } + m_done = true; + closeOutput(); + + SoundMetaData md(m_encodedSize, last_md.relativeTimestamp(), last_md.absoluteTimestamp(), m_outputURL); + TQApplication::postEvent(m_parent, new SoundStreamEncodingStepEvent(m_SoundStreamID, NULL, 0, md)); + + TQApplication::postEvent(m_parent, new SoundStreamEncodingTerminatedEvent(m_SoundStreamID)); +} + diff --git a/tderadio3/plugins/recording/encoder.h b/tderadio3/plugins/recording/encoder.h new file mode 100644 index 0000000..b0c442d --- /dev/null +++ b/tderadio3/plugins/recording/encoder.h @@ -0,0 +1,101 @@ +/*************************************************************************** + encoder.h - description + ------------------- + begin : Thu May 05 2005 + copyright : (C) 2005 by Martin Witte + email : witte@kawo1.rwth-aachen.de + ***************************************************************************/ + +/*************************************************************************** + * * + * 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. * + * * + ***************************************************************************/ + +#ifndef KRADIO_RECORDING_ENCODER_H +#define KRADIO_RECORDING_ENCODER_H + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + + +#include <tqobject.h> +#include <tqstring.h> +#include <tqthread.h> + +#include "../../src/include/radiostation.h" +#include "../../src/include/multibuffer.h" +#include "../../src/include/sound_metadata.h" +#include "../../src/include/soundstreamid.h" +#include "recording-config.h" + +class BufferSoundMetaData : public SoundMetaData +{ +public: + BufferSoundMetaData() + : SoundMetaData(0, 0, 0, KURL()), m_BufferPosition(0) {} + BufferSoundMetaData(const SoundMetaData &md, size_t bufferpos) + : SoundMetaData(md), m_BufferPosition(bufferpos) {} + BufferSoundMetaData(TQ_INT64 pos, time_t rel, time_t abs, const KURL &url, size_t bufferpos) + : SoundMetaData(pos, rel, abs, url), m_BufferPosition(bufferpos) {} + + size_t bufferPosition() const { return m_BufferPosition; } + +protected: + size_t m_BufferPosition; +}; + + +class RecordingEncoding : public TQThread +{ +public: + RecordingEncoding(TQObject *parent, SoundStreamID id, const RecordingConfig &cfg, const RadioStation *rs, const TQString &filename); + virtual ~RecordingEncoding(); + + void run(); + + char *lockInputBuffer(size_t &bufferSize); // bytes we whish to write, returns number of bytes available + void unlockInputBuffer(size_t bufferSize, const SoundMetaData &md); // bytes we actually wrote + + bool error() const { return m_error; } + const TQString &errorString() const { return m_errorString; } + + void setDone(); + bool IsDone() { return m_done; } + + virtual bool openOutput(const TQString &outputFile) = 0; + virtual void closeOutput() = 0; + + TQ_UINT64 encodedSize() const { return m_encodedSize; } + + const RecordingConfig &config() const { return m_config; } + +protected: + virtual void encode(const char *_buffer, size_t buffer_size, char *&export_buffer, size_t &export_buffer_size) = 0; + + TQObject *m_parent; + RecordingConfig m_config; + RadioStation *m_RadioStation; + SoundStreamID m_SoundStreamID; + + bool m_error; + TQString m_errorString; + bool m_done; + + MultiBuffer m_InputBuffers; + TQPtrList<BufferSoundMetaData> + **m_buffersMetaData; + TQ_UINT64 m_encodedSize; + + time_t m_InputStartTime; + TQ_UINT64 m_InputStartPosition; + + KURL m_outputURL; +}; + + +#endif diff --git a/tderadio3/plugins/recording/encoder_mp3.cpp b/tderadio3/plugins/recording/encoder_mp3.cpp new file mode 100644 index 0000000..2ec1e1d --- /dev/null +++ b/tderadio3/plugins/recording/encoder_mp3.cpp @@ -0,0 +1,214 @@ +/*************************************************************************** + encoder_mp3.cpp + ------------------- + begin : Sat Aug 20 2005 + copyright : (C) 2005 by Martin Witte + email : witte@kawo1.rwth-aachen.de + ***************************************************************************/ + +/*************************************************************************** + * * + * 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 "encoder_mp3.h" + +#include <tqmutex.h> +#include <klocale.h> + +RecordingEncodingMP3::RecordingEncodingMP3(TQObject *parent, SoundStreamID ssid, + const RecordingConfig &cfg, const RadioStation *rs, + const TQString &filename) + : RecordingEncoding(parent, ssid, cfg, rs, filename) +#ifdef HAVE_LAME + , + m_MP3Buffer(NULL), + m_MP3BufferSize(0), + m_MP3Output(NULL), + m_ID3Tags(NULL), + m_LAMEFlags(NULL), + m_MP3LBuffer(NULL), + m_MP3RBuffer(NULL) +#endif +{ + m_config.m_OutputFormat = RecordingConfig::outputMP3; + m_config.m_SoundFormat.m_Encoding = "mp3"; + openOutput(filename); +} + + +RecordingEncodingMP3::~RecordingEncodingMP3() +{ + closeOutput(); +} + + +static TQMutex lameSerialization; + +void RecordingEncodingMP3::encode(const char *_buffer, size_t buffer_size, char *&export_buffer, size_t &export_buffer_size) +{ + if (m_error) + return; + +#ifdef HAVE_LAME + short int *buffer = (short int*)_buffer; + size_t j = 0, + j_inc = (m_config.m_SoundFormat.m_Channels == 1) ? 1 : 2, + dj = (m_config.m_SoundFormat.m_Channels == 1) ? 0 : 1, + samples = buffer_size / m_config.m_SoundFormat.frameSize(); + + for (size_t i = 0; i < samples; ++i, j+=j_inc) { + m_MP3LBuffer[i] = buffer[j]; + m_MP3RBuffer[i] = buffer[j+dj]; + } + + int n = 0; + lameSerialization.lock(); + n = lame_encode_buffer(m_LAMEFlags, + m_MP3LBuffer, + m_MP3RBuffer, + samples, + m_MP3Buffer, + m_MP3BufferSize); + lameSerialization.unlock(); + if (n < 0) { + m_errorString += i18n("Error %1 while encoding mp3. ").arg(TQString().setNum(n)); + m_error = true; + } else if (n > 0) { + m_encodedSize += n; + + export_buffer = (char*)m_MP3Buffer; + export_buffer_size = n; + int r = fwrite(m_MP3Buffer, 1, n, m_MP3Output); + + if (r <= 0) { + m_errorString += i18n("Error %1 writing output. ").arg(TQString().setNum(r)); + m_error = true; + } + } +#endif +} + + + +bool RecordingEncodingMP3::openOutput(const TQString &output) +{ +#ifdef HAVE_LAME +// m_output = NULL; + m_LAMEFlags = lame_init(); + + if (!m_LAMEFlags) { + m_error = true; + m_errorString += i18n("Cannot initialize lalibmp3lame. "); + } else { + lame_set_in_samplerate(m_LAMEFlags, m_config.m_SoundFormat.m_SampleRate); + lame_set_num_channels(m_LAMEFlags, 2); + //lame_set_quality(m_LAMEFlags, m_config.mp3Quality); + + lame_set_mode(m_LAMEFlags, m_config.m_SoundFormat.m_Channels == 1 ? MONO : JOINT_STEREO); + + // lame_seterrorf(m_LAMEFlags, ...); + // lame_setdebugf(m_LAMEFlags, ...); + // lame_setmsgf(m_LAMEFlags, ...); + + lame_set_VBR(m_LAMEFlags, vbr_default); + lame_set_VBR_q(m_LAMEFlags, m_config.m_mp3Quality); + + if (lame_init_params(m_LAMEFlags) < 0) { + m_error = true; + m_errorString += i18n("Cannot initialize libmp3lame parameters. ").arg(output); + } + + if (!m_error) { + id3tag_init(m_LAMEFlags); + id3tag_add_v2(m_LAMEFlags); + TQString title = m_RadioStation->name() + TQString().sprintf(" - %s", (TQDateTime::currentDateTime().toString(Qt::ISODate)).ascii()); + TQString comment = i18n("Recorded by TDERadio"); + size_t l = title.length() + comment.length() + 10; + m_ID3Tags = new char[l]; + char *ctitle = m_ID3Tags; + strcpy(ctitle, title.latin1()); + char *ccomment = m_ID3Tags + strlen(ctitle) + 1; + strcpy(ccomment, comment.latin1()); + id3tag_set_title(m_LAMEFlags, ctitle); + id3tag_set_comment(m_LAMEFlags, ccomment); + } + + m_MP3Output = fopen(output.ascii(), "wb+"); + if (!m_MP3Output) { + m_errorString += i18n("Cannot open output file %1. ").arg(output); + m_error = true; + } + + size_t nSamples = m_config.m_EncodeBufferSize / m_config.m_SoundFormat.frameSize(); + m_MP3BufferSize = nSamples + nSamples / 4 + 7200; + m_MP3Buffer = new unsigned char[m_MP3BufferSize]; + + m_MP3LBuffer = new short int[nSamples]; + m_MP3RBuffer = new short int[nSamples]; + + if (!m_MP3Buffer || !m_MP3LBuffer || !m_MP3RBuffer) { + m_error = true; + m_errorString += i18n("Cannot allocate buffers for mp3 encoding. "); + } + } + + if (m_error) { + if (m_LAMEFlags) lame_close(m_LAMEFlags); + m_LAMEFlags = NULL; + if (m_MP3Output) fclose(m_MP3Output); + m_MP3Output = NULL; + if (m_MP3Buffer) delete [] m_MP3Buffer; + m_MP3Buffer = NULL; + m_MP3BufferSize = 0; + if (m_ID3Tags) delete [] m_ID3Tags; + m_ID3Tags = NULL; + if (m_MP3LBuffer) delete[] m_MP3LBuffer; + if (m_MP3RBuffer) delete[] m_MP3RBuffer; + m_MP3LBuffer = m_MP3RBuffer = NULL; + } +#endif + return !m_error; +} + + +void RecordingEncodingMP3::closeOutput() +{ +#ifdef HAVE_LAME + if (m_LAMEFlags) { + if (m_config.m_OutputFormat == RecordingConfig::outputMP3) { + int n = lame_encode_flush(m_LAMEFlags, + m_MP3Buffer, + m_MP3BufferSize); + if (n < 0) { + m_error = true; + m_errorString += i18n("Error %1 while encoding mp3. ").arg(TQString().setNum(n)); + } else if (n > 0) { + int r = fwrite(m_MP3Buffer, 1, n, m_MP3Output); + if (r <= 0) { + m_error = true; + m_errorString += i18n("Error %1 writing output. ").arg(TQString().setNum(r)); + } else { + lame_mp3_tags_fid(m_LAMEFlags, m_MP3Output); + } + } + } + if (m_LAMEFlags) lame_close(m_LAMEFlags); + m_LAMEFlags = NULL; + if (m_MP3Output) fclose(m_MP3Output); + m_MP3Output = NULL; + m_MP3BufferSize = 0; + if (m_MP3Buffer) delete [] m_MP3Buffer; + m_MP3Buffer = NULL; + if (m_ID3Tags) delete [] m_ID3Tags; + m_ID3Tags = NULL; + if (m_MP3LBuffer) delete[] m_MP3LBuffer; + if (m_MP3RBuffer) delete[] m_MP3RBuffer; + m_MP3LBuffer = m_MP3RBuffer = NULL; + } +#endif +} diff --git a/tderadio3/plugins/recording/encoder_mp3.h b/tderadio3/plugins/recording/encoder_mp3.h new file mode 100644 index 0000000..aaa912c --- /dev/null +++ b/tderadio3/plugins/recording/encoder_mp3.h @@ -0,0 +1,56 @@ +/*************************************************************************** + encoder_mp3.h + ------------------- + begin : Sat Aug 20 2005 + copyright : (C) 2005 by Martin Witte + email : witte@kawo1.rwth-aachen.de + ***************************************************************************/ + +/*************************************************************************** + * * + * 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. * + * * + ***************************************************************************/ + +#ifndef KRADIO_RECORDING_ENCODER_MP3_H +#define KRADIO_RECORDING_ENCODER_MP3_H + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include "encoder.h" + +#ifdef HAVE_LAME + #include <lame/lame.h> +#endif + +class RecordingEncodingMP3 : public RecordingEncoding +{ +public: + RecordingEncodingMP3(TQObject *parent, SoundStreamID id, const RecordingConfig &cfg, const RadioStation *rs, const TQString &filename); + virtual ~RecordingEncodingMP3(); + + bool openOutput(const TQString &outputFile); + void closeOutput(); + +protected: + void encode(const char *_buffer, size_t buffer_size, char *&export_buffer, size_t &export_buffer_size); + +#ifdef HAVE_LAME + unsigned char *m_MP3Buffer; + size_t m_MP3BufferSize; + FILE *m_MP3Output; + char *m_ID3Tags; + lame_global_flags *m_LAMEFlags; + short int *m_MP3LBuffer, + *m_MP3RBuffer; +#endif +}; + + + +#endif diff --git a/tderadio3/plugins/recording/encoder_ogg.cpp b/tderadio3/plugins/recording/encoder_ogg.cpp new file mode 100644 index 0000000..fa61ab1 --- /dev/null +++ b/tderadio3/plugins/recording/encoder_ogg.cpp @@ -0,0 +1,250 @@ +/*************************************************************************** + encoder_ogg.cpp + ------------------- + begin : Sat Aug 20 2005 + copyright : (C) 2005 by Martin Witte + email : witte@kawo1.rwth-aachen.de + ***************************************************************************/ + +/*************************************************************************** + * * + * 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 "encoder_ogg.h" + +#include <klocale.h> +#include <stdlib.h> + +RecordingEncodingOgg::RecordingEncodingOgg(TQObject *parent, SoundStreamID ssid, + const RecordingConfig &cfg, const RadioStation *rs, + const TQString &filename) + : RecordingEncoding(parent, ssid, cfg, rs, filename) +#ifdef HAVE_OGG + , + m_OggOutput(NULL), + m_OggExportBuffer(NULL), + m_OggExportBufferSize(0) +#endif +{ + m_config.m_OutputFormat = RecordingConfig::outputOGG; + m_config.m_SoundFormat.m_Encoding = "ogg"; + openOutput(filename); +} + + +RecordingEncodingOgg::~RecordingEncodingOgg() +{ + closeOutput(); +} + +void RecordingEncodingOgg::encode(const char *_buffer, size_t buffer_size, char *&export_buffer, size_t &export_buffer_size) +{ + if (m_error) + return; + +#ifdef HAVE_OGG + SoundFormat &sf = m_config.m_SoundFormat; + ogg_page ogg_pg; + ogg_packet ogg_pkt; + + size_t samples = buffer_size / sf.frameSize(); + + // buffer[channel][sample], normalized to -1..0..+1 + float **buffer = vorbis_analysis_buffer(&m_VorbisDSP, (samples < 512 ? 512 : samples)); + + sf.convertSamplesToFloat(_buffer, buffer, samples); + + /* Tell the library how many samples (per channel) we wrote + into the supplied buffer */ + vorbis_analysis_wrote(&m_VorbisDSP, samples); + + /* While we can get enough data from the library to analyse, one + block at a time... */ + + bool eos = false; + while(!m_error && !eos && vorbis_analysis_blockout(&m_VorbisDSP, &m_VorbisBlock) == 1) { + + /* Do the main analysis, creating a packet */ + vorbis_analysis(&m_VorbisBlock, NULL); + vorbis_bitrate_addblock(&m_VorbisBlock); + + while(!m_error && vorbis_bitrate_flushpacket(&m_VorbisDSP, &ogg_pkt)) { + /* Add packet to bitstream */ + ogg_stream_packetin(&m_OggStream,&ogg_pkt); + + /* If we've gone over a page boundary, we can do actual output, + so do so (for however many pages are available) */ + + while(!m_error && !eos) { + int result = ogg_stream_pageout(&m_OggStream, &ogg_pg); + if (!result) break; + + int n = fwrite(ogg_pg.header, 1, ogg_pg.header_len, m_OggOutput); + n += fwrite(ogg_pg.body, 1, ogg_pg.body_len, m_OggOutput); + + m_encodedSize += n; + + if (n != (ogg_pg.header_len + ogg_pg.body_len)) { + m_error = true; + m_errorString += i18n("Failed writing data to ogg/vorbis output stream. "); + break; + } else { + + if (m_OggExportBufferSize < export_buffer_size + n) { + m_OggExportBuffer = (char*)realloc(m_OggExportBuffer, m_OggExportBufferSize + 2 * n); + m_OggExportBufferSize += 2 * n; + } + + memcpy (m_OggExportBuffer + export_buffer_size, ogg_pg.header, ogg_pg.header_len); + export_buffer_size += ogg_pg.header_len; + memcpy (m_OggExportBuffer + export_buffer_size, ogg_pg.body, ogg_pg.body_len); + export_buffer_size += ogg_pg.body_len; + + } + if (ogg_page_eos(&ogg_pg)) + eos = 1; + } + } + } + + export_buffer = m_OggExportBuffer; +#endif +} + + +#ifdef HAVE_OGG +static void vorbis_comment_add_tag_new(vorbis_comment *vc, const TQString &tag, const TQString &value) +{ + char *stag = strdup(tag.ascii()); + char *svalue = strdup(value.utf8()); + vorbis_comment_add_tag(vc, stag, svalue); + delete stag; + delete svalue; +} +#endif + +bool RecordingEncodingOgg::openOutput(const TQString &output) +{ +#ifdef HAVE_OGG + m_OggOutput = fopen(output.ascii(), "wb+"); + if (!m_OggOutput) { + m_errorString += i18n("Cannot open Ogg/Vorbis output file %1. ").arg(output); + m_error = true; + } + + m_OggExportBuffer = (char*)malloc(m_OggExportBufferSize = 65536); // start with a 64k buffer + + + /* Have vorbisenc choose a mode for us */ + vorbis_info_init(&m_VorbisInfo); + + SoundFormat &sf = m_config.m_SoundFormat; + if (vorbis_encode_setup_vbr(&m_VorbisInfo, sf.m_Channels, sf.m_SampleRate, m_config.m_oggQuality)) { + m_error = true; + m_errorString = i18n("Ogg/Vorbis Mode initialisation failed: invalid parameters for quality\n"); + vorbis_info_clear(&m_VorbisInfo); + return false; + } + + /* Turn off management entirely (if it was turned on). */ + vorbis_encode_ctl(&m_VorbisInfo, OV_ECTL_RATEMANAGE_SET, NULL); + vorbis_encode_setup_init(&m_VorbisInfo); + + /* Now, set up the analysis engine, stream encoder, and other + preparation before the encoding begins. + */ + + vorbis_analysis_init(&m_VorbisDSP, &m_VorbisInfo); + vorbis_block_init(&m_VorbisDSP, &m_VorbisBlock); + + ogg_stream_init (&m_OggStream, m_SoundStreamID.getID()); + + /* Now, build the three header packets and send through to the stream + output stage (but defer actual file output until the main encode loop) */ + + ogg_packet header_main; + ogg_packet header_comments; + ogg_packet header_codebooks; + + /* Build the packets */ + vorbis_comment vc; + vorbis_comment_init (&vc); + vorbis_comment_add_tag_new(&vc, "creator", "TDERadio" VERSION); + vorbis_comment_add_tag_new(&vc, "title", m_RadioStation->longName().utf8()); + vorbis_comment_add_tag_new(&vc, "date", TQDateTime::currentDateTime().toString(Qt::ISODate)); + + vorbis_analysis_headerout(&m_VorbisDSP, &vc, + &header_main, &header_comments, &header_codebooks); + + /* And stream them out */ + ogg_stream_packetin(&m_OggStream, &header_main); + ogg_stream_packetin(&m_OggStream, &header_comments); + ogg_stream_packetin(&m_OggStream, &header_codebooks); + + int result; + ogg_page ogg_page; + while((result = ogg_stream_flush(&m_OggStream, &ogg_page))) { + + if (!result) break; + + int n = fwrite(ogg_page.header, 1, ogg_page.header_len, m_OggOutput); + n += fwrite(ogg_page.body, 1, ogg_page.body_len, m_OggOutput); + + if(n != ogg_page.header_len + ogg_page.body_len) { + m_error = true; + m_errorString += i18n("Failed writing Ogg/Vorbis header to output stream\n"); + break; + } + } + + vorbis_comment_clear (&vc); + + if (m_error) { + if (m_OggOutput) fclose (m_OggOutput); + m_OggOutput = NULL; + free(m_OggExportBuffer); + m_OggExportBuffer = NULL; + m_OggExportBufferSize = 0; + + ogg_stream_clear(&m_OggStream); + vorbis_block_clear(&m_VorbisBlock); + vorbis_dsp_clear(&m_VorbisDSP); + vorbis_info_clear(&m_VorbisInfo); + } + + return !m_error; +#endif +} + + +void RecordingEncodingOgg::closeOutput() +{ +#ifdef HAVE_OGG + if (m_OggOutput) { + + char *tmp_buf = NULL; + size_t tmp_size = 0; + // flush buffer + encode(tmp_buf, tmp_size, tmp_buf, tmp_size); + + fclose(m_OggOutput); + m_OggOutput = NULL; + + free(m_OggExportBuffer); + m_OggExportBuffer = NULL; + m_OggExportBufferSize = 0; + + ogg_stream_clear(&m_OggStream); + vorbis_block_clear(&m_VorbisBlock); + vorbis_dsp_clear(&m_VorbisDSP); + vorbis_info_clear(&m_VorbisInfo); + } +#endif +} + + diff --git a/tderadio3/plugins/recording/encoder_ogg.h b/tderadio3/plugins/recording/encoder_ogg.h new file mode 100644 index 0000000..586c96e --- /dev/null +++ b/tderadio3/plugins/recording/encoder_ogg.h @@ -0,0 +1,55 @@ +/*************************************************************************** + encoder_ogg.h + ------------------- + begin : Sat Aug 20 2005 + copyright : (C) 2005 by Martin Witte + email : witte@kawo1.rwth-aachen.de + ***************************************************************************/ + +/*************************************************************************** + * * + * 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. * + * * + ***************************************************************************/ + +#ifndef KRADIO_RECORDING_ENCODER_OGG_H +#define KRADIO_RECORDING_ENCODER_OGG_H + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include "encoder.h" + +#ifdef HAVE_OGG + #include <vorbis/vorbisenc.h> +#endif + +class RecordingEncodingOgg : public RecordingEncoding +{ +public: + RecordingEncodingOgg(TQObject *parent, SoundStreamID id, const RecordingConfig &cfg, const RadioStation *rs, const TQString &filename); + virtual ~RecordingEncodingOgg(); + + bool openOutput(const TQString &outputFile); + void closeOutput(); + +protected: + void encode(const char *_buffer, size_t buffer_size, char *&export_buffer, size_t &export_buffer_size); + +#ifdef HAVE_OGG + FILE *m_OggOutput; + char *m_OggExportBuffer; + size_t m_OggExportBufferSize; + ogg_stream_state m_OggStream; + vorbis_dsp_state m_VorbisDSP; + vorbis_block m_VorbisBlock; + vorbis_info m_VorbisInfo; +#endif +}; + + +#endif diff --git a/tderadio3/plugins/recording/encoder_pcm.cpp b/tderadio3/plugins/recording/encoder_pcm.cpp new file mode 100644 index 0000000..1143bc3 --- /dev/null +++ b/tderadio3/plugins/recording/encoder_pcm.cpp @@ -0,0 +1,78 @@ +/*************************************************************************** + encoder_pcm.cpp + ------------------- + begin : Sat Aug 20 2005 + copyright : (C) 2005 by Martin Witte + email : witte@kawo1.rwth-aachen.de + ***************************************************************************/ + +/*************************************************************************** + * * + * 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 "encoder_pcm.h" + + +#include <klocale.h> + +RecordingEncodingPCM::RecordingEncodingPCM(TQObject *parent, SoundStreamID ssid, + const RecordingConfig &cfg, const RadioStation *rs, + const TQString &filename) + : RecordingEncoding(parent, ssid, cfg, rs, filename), + m_output(NULL) +{ + m_config.m_SoundFormat.m_Encoding = "raw"; + openOutput(filename); +} + + +RecordingEncodingPCM::~RecordingEncodingPCM() +{ + closeOutput(); +} + + + +void RecordingEncodingPCM::encode(const char *buffer, size_t buffer_size, char *&export_buffer, size_t &export_buffer_size) +{ + if (m_error) + return; + m_encodedSize += buffer_size; + + export_buffer = const_cast<char*>(buffer); + export_buffer_size = buffer_size; + int err = sf_write_raw(m_output, const_cast<char*>(buffer), buffer_size); + + if (err != (int)buffer_size) { + m_error = true; + m_errorString += i18n("Error %1 writing output. ").arg(TQString().setNum(err)); + } +} + + +bool RecordingEncodingPCM::openOutput(const TQString &output) +{ + SF_INFO sinfo; + m_config.getSoundFileInfo(sinfo, false); + m_output = sf_open(output.ascii(), SFM_WRITE, &sinfo); + + if (!m_output) { + m_error = true; + m_errorString += i18n("Cannot open output file %1. ").arg(output); + } + return !m_error; +} + + +void RecordingEncodingPCM::closeOutput() +{ + if (m_output) sf_close (m_output); + m_output = NULL; +} + + diff --git a/tderadio3/plugins/recording/encoder_pcm.h b/tderadio3/plugins/recording/encoder_pcm.h new file mode 100644 index 0000000..40e6cf4 --- /dev/null +++ b/tderadio3/plugins/recording/encoder_pcm.h @@ -0,0 +1,46 @@ +/*************************************************************************** + encoder_pcm.h + ------------------- + begin : Sat Aug 20 2005 + copyright : (C) 2005 by Martin Witte + email : witte@kawo1.rwth-aachen.de + ***************************************************************************/ + +/*************************************************************************** + * * + * 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. * + * * + ***************************************************************************/ + +#ifndef KRADIO_RECORDING_ENCODER_PCM_H +#define KRADIO_RECORDING_ENCODER_PCM_H + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include "encoder.h" + +#include <sndfile.h> + +class RecordingEncodingPCM : public RecordingEncoding +{ +public: + RecordingEncodingPCM(TQObject *parent, SoundStreamID id, const RecordingConfig &cfg, const RadioStation *rs, const TQString &filename); + virtual ~RecordingEncodingPCM(); + + bool openOutput(const TQString &outputFile); + void closeOutput(); + +protected: + void encode(const char *_buffer, size_t buffer_size, char *&export_buffer, size_t &export_buffer_size); + + + SNDFILE *m_output; +}; + + +#endif diff --git a/tderadio3/plugins/recording/icons/Makefile.am b/tderadio3/plugins/recording/icons/Makefile.am new file mode 100644 index 0000000..b3f2583 --- /dev/null +++ b/tderadio3/plugins/recording/icons/Makefile.am @@ -0,0 +1,2 @@ +icons_ICON = AUTO +iconsdir = $(kde_datadir)/kradio/icons diff --git a/tderadio3/plugins/recording/icons/hi16-action-tderadio_record.png b/tderadio3/plugins/recording/icons/hi16-action-tderadio_record.png Binary files differnew file mode 100644 index 0000000..0899344 --- /dev/null +++ b/tderadio3/plugins/recording/icons/hi16-action-tderadio_record.png diff --git a/tderadio3/plugins/recording/icons/hi16-app-tderadio_plus_rec.png b/tderadio3/plugins/recording/icons/hi16-app-tderadio_plus_rec.png Binary files differnew file mode 100644 index 0000000..ce39582 --- /dev/null +++ b/tderadio3/plugins/recording/icons/hi16-app-tderadio_plus_rec.png diff --git a/tderadio3/plugins/recording/icons/hi22-action-tderadio_record.png b/tderadio3/plugins/recording/icons/hi22-action-tderadio_record.png Binary files differnew file mode 100644 index 0000000..a39d11e --- /dev/null +++ b/tderadio3/plugins/recording/icons/hi22-action-tderadio_record.png diff --git a/tderadio3/plugins/recording/icons/hi22-app-tderadio_plus_rec.png b/tderadio3/plugins/recording/icons/hi22-app-tderadio_plus_rec.png Binary files differnew file mode 100644 index 0000000..2167b93 --- /dev/null +++ b/tderadio3/plugins/recording/icons/hi22-app-tderadio_plus_rec.png diff --git a/tderadio3/plugins/recording/icons/hi256-action-tderadio_record.png b/tderadio3/plugins/recording/icons/hi256-action-tderadio_record.png Binary files differnew file mode 100644 index 0000000..ccafd20 --- /dev/null +++ b/tderadio3/plugins/recording/icons/hi256-action-tderadio_record.png diff --git a/tderadio3/plugins/recording/icons/hi32-action-tderadio_record.png b/tderadio3/plugins/recording/icons/hi32-action-tderadio_record.png Binary files differnew file mode 100644 index 0000000..f6f09ac --- /dev/null +++ b/tderadio3/plugins/recording/icons/hi32-action-tderadio_record.png diff --git a/tderadio3/plugins/recording/icons/hi32-app-tderadio_plus_rec.png b/tderadio3/plugins/recording/icons/hi32-app-tderadio_plus_rec.png Binary files differnew file mode 100644 index 0000000..819d395 --- /dev/null +++ b/tderadio3/plugins/recording/icons/hi32-app-tderadio_plus_rec.png diff --git a/tderadio3/plugins/recording/icons/hi48-action-tderadio_record.png b/tderadio3/plugins/recording/icons/hi48-action-tderadio_record.png Binary files differnew file mode 100644 index 0000000..06b0603 --- /dev/null +++ b/tderadio3/plugins/recording/icons/hi48-action-tderadio_record.png diff --git a/tderadio3/plugins/recording/icons/hi48-app-tderadio_plus_rec.png b/tderadio3/plugins/recording/icons/hi48-app-tderadio_plus_rec.png Binary files differnew file mode 100644 index 0000000..c469ee4 --- /dev/null +++ b/tderadio3/plugins/recording/icons/hi48-app-tderadio_plus_rec.png diff --git a/tderadio3/plugins/recording/icons/hi64-action-tderadio_record.png b/tderadio3/plugins/recording/icons/hi64-action-tderadio_record.png Binary files differnew file mode 100644 index 0000000..2027123 --- /dev/null +++ b/tderadio3/plugins/recording/icons/hi64-action-tderadio_record.png diff --git a/tderadio3/plugins/recording/icons/hi64-app-tderadio_plus_rec.png b/tderadio3/plugins/recording/icons/hi64-app-tderadio_plus_rec.png Binary files differnew file mode 100644 index 0000000..99825d7 --- /dev/null +++ b/tderadio3/plugins/recording/icons/hi64-app-tderadio_plus_rec.png diff --git a/tderadio3/plugins/recording/po/Makefile.am b/tderadio3/plugins/recording/po/Makefile.am new file mode 100644 index 0000000..80443c3 --- /dev/null +++ b/tderadio3/plugins/recording/po/Makefile.am @@ -0,0 +1,2 @@ +PACKAGE = kradio-recording +POFILES = AUTO diff --git a/tderadio3/plugins/recording/po/de.po b/tderadio3/plugins/recording/po/de.po new file mode 100644 index 0000000..f222057 --- /dev/null +++ b/tderadio3/plugins/recording/po/de.po @@ -0,0 +1,435 @@ +# translation of de.po to +# translation of kradio-recording.po to +# This file is put in the public domain. +# +# Ernst Martin Witte <emw@nocabal.de>, 2006. +msgid "" +msgstr "" +"Project-Id-Version: de\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2006-11-11 18:43+0100\n" +"PO-Revision-Date: 2006-11-06 00:57+0100\n" +"Last-Translator: Ernst Martin Witte <emw@nocabal.de>\n" +"Language-Team: <de@li.org>\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"X-Generator: KBabel 1.11.4\n" + +#. i18n: file recording-configuration-ui.ui line 16 +#: rc.cpp:3 rc.cpp:117 recording-configuration-ui.cpp:244 +#, no-c-format +msgid "RecordingConfigurationUI" +msgstr "RecordingConfigurationUI" + +#. i18n: file recording-configuration-ui.ui line 34 +#: rc.cpp:6 rc.cpp:120 recording-configuration-ui.cpp:256 +#, no-c-format +msgid "Output" +msgstr "Ausgabe" + +#. i18n: file recording-configuration-ui.ui line 138 +#: rc.cpp:9 rc.cpp:123 recording-configuration-ui.cpp:245 +#, no-c-format +msgid "MP3 Quality(0 - high, 9 - low)" +msgstr "MP3 Qualität(0 - hoch, 9 - niedrig)" + +#. i18n: file recording-configuration-ui.ui line 149 +#: rc.cpp:12 rc.cpp:126 recording-configuration-ui.cpp:247 +#, no-c-format +msgid "raw pcm output (.raw)" +msgstr "reine PCM-Ausgabe (.raw)" + +#. i18n: file recording-configuration-ui.ui line 154 +#: rc.cpp:15 rc.cpp:129 recording-configuration-ui.cpp:248 +#, no-c-format +msgid "Microsoft Wave (.wav)" +msgstr "Microsoft Wave (.wav)" + +#. i18n: file recording-configuration-ui.ui line 159 +#: rc.cpp:18 rc.cpp:132 recording-configuration-ui.cpp:249 +#, no-c-format +msgid "Apple/SGI (.aiff)" +msgstr "Apple/SGI (.aiff)" + +#. i18n: file recording-configuration-ui.ui line 164 +#: rc.cpp:21 rc.cpp:135 recording-configuration-ui.cpp:250 +#, no-c-format +msgid "Sun/NeXT (.au)" +msgstr "Sun/NeXT (.au)" + +#. i18n: file recording-configuration-ui.ui line 169 +#: rc.cpp:24 rc.cpp:138 recording-configuration-ui.cpp:251 +#, no-c-format +msgid "MP3 Compressed (.mp3)" +msgstr "MP3 komprimiert (.mp3)" + +#. i18n: file recording-configuration-ui.ui line 174 +#: rc.cpp:27 rc.cpp:141 recording-configuration-ui.cpp:252 +#, no-c-format +msgid "Ogg/Vorbis Compressed (.ogg)" +msgstr "Ogg/Vorbis komprimiert (.ogg)" + +#. i18n: file recording-configuration-ui.ui line 194 +#: rc.cpp:30 rc.cpp:144 recording-configuration-ui.cpp:253 +#, no-c-format +msgid "Recording Directory" +msgstr "Aufnahme-Verzeichnis" + +#. i18n: file recording-configuration-ui.ui line 202 +#: rc.cpp:33 rc.cpp:147 recording-configuration-ui.cpp:254 +#, no-c-format +msgid "File Format" +msgstr "Dateiformat" + +#. i18n: file recording-configuration-ui.ui line 210 +#: rc.cpp:36 rc.cpp:150 recording-configuration-ui.cpp:255 +#, no-c-format +msgid "Ogg Quality(0 - low, 9 - high)" +msgstr "Ogg Qualität(0 - niedrig, 9 - hoch)" + +#. i18n: file recording-configuration-ui.ui line 239 +#: rc.cpp:39 rc.cpp:153 recording-configuration-ui.cpp:278 +#, no-c-format +msgid "I&nput" +msgstr "Quelle" + +#. i18n: file recording-configuration-ui.ui line 276 +#: rc.cpp:42 rc.cpp:156 recording-configuration-ui.cpp:258 +#, no-c-format +msgid "48000" +msgstr "48000" + +#. i18n: file recording-configuration-ui.ui line 281 +#: rc.cpp:45 rc.cpp:159 recording-configuration-ui.cpp:259 +#, no-c-format +msgid "44100" +msgstr "44100" + +#. i18n: file recording-configuration-ui.ui line 286 +#: rc.cpp:48 rc.cpp:162 recording-configuration-ui.cpp:260 +#, no-c-format +msgid "22050" +msgstr "22050" + +#. i18n: file recording-configuration-ui.ui line 291 +#: rc.cpp:51 rc.cpp:165 recording-configuration-ui.cpp:261 +#, no-c-format +msgid "11025" +msgstr "11025" + +#. i18n: file recording-configuration-ui.ui line 311 +#: rc.cpp:54 rc.cpp:168 recording-configuration-ui.cpp:262 +#, no-c-format +msgid "Endianess" +msgstr "Byte-Reihenfolge" + +#. i18n: file recording-configuration-ui.ui line 334 +#: rc.cpp:57 rc.cpp:171 recording-configuration-ui.cpp:264 +#, no-c-format +msgid "Stereo" +msgstr "Stereo" + +#. i18n: file recording-configuration-ui.ui line 339 +#: rc.cpp:60 rc.cpp:174 recording-configuration-ui.cpp:265 +#, no-c-format +msgid "Mono" +msgstr "Mono" + +#. i18n: file recording-configuration-ui.ui line 359 +#: rc.cpp:63 rc.cpp:177 recording-configuration-ui.cpp:266 +#: recording-monitor.cpp:53 +#, no-c-format +msgid "Sample Rate" +msgstr "Abtastrate" + +#. i18n: file recording-configuration-ui.ui line 365 +#: rc.cpp:66 rc.cpp:180 recording-configuration-ui.cpp:268 +#, no-c-format +msgid "Little Endian" +msgstr "Little Endian" + +#. i18n: file recording-configuration-ui.ui line 370 +#: rc.cpp:69 rc.cpp:183 recording-configuration-ui.cpp:269 +#, no-c-format +msgid "Big Endian" +msgstr "Big Endian" + +#. i18n: file recording-configuration-ui.ui line 388 +#: rc.cpp:72 rc.cpp:186 recording-configuration-ui.cpp:271 +#, no-c-format +msgid "16" +msgstr "16" + +#. i18n: file recording-configuration-ui.ui line 393 +#: rc.cpp:75 rc.cpp:189 recording-configuration-ui.cpp:272 +#, no-c-format +msgid "8" +msgstr "8" + +#. i18n: file recording-configuration-ui.ui line 413 +#: rc.cpp:78 rc.cpp:192 recording-configuration-ui.cpp:273 +#, no-c-format +msgid "Channels" +msgstr "Kanäle" + +#. i18n: file recording-configuration-ui.ui line 421 +#: rc.cpp:81 rc.cpp:195 recording-configuration-ui.cpp:274 +#, no-c-format +msgid "Sample Bits" +msgstr "Quantisierungs-Bits" + +#. i18n: file recording-configuration-ui.ui line 427 +#: rc.cpp:84 rc.cpp:198 recording-configuration-ui.cpp:276 +#, no-c-format +msgid "Signed" +msgstr "Vorzeichenbehaftet" + +#. i18n: file recording-configuration-ui.ui line 432 +#: rc.cpp:87 rc.cpp:201 recording-configuration-ui.cpp:277 +#, no-c-format +msgid "Unsigned" +msgstr "Vorzeichenlos" + +#. i18n: file recording-configuration-ui.ui line 490 +#: rc.cpp:90 rc.cpp:204 recording-configuration-ui.cpp:282 +#, no-c-format +msgid "&Buffers" +msgstr "&Puffer" + +#. i18n: file recording-configuration-ui.ui line 512 +#: rc.cpp:93 rc.cpp:207 recording-configuration-ui.cpp:279 +#, no-c-format +msgid " kB" +msgstr " kB" + +#. i18n: file recording-configuration-ui.ui line 532 +#: rc.cpp:96 rc.cpp:210 recording-configuration-ui.cpp:280 +#, no-c-format +msgid "Encoding Buffer Size" +msgstr "Codierungs-Puffergröße" + +#. i18n: file recording-configuration-ui.ui line 551 +#: rc.cpp:99 rc.cpp:213 recording-configuration-ui.cpp:281 +#, no-c-format +msgid "Number of Buffers" +msgstr "Anzahl der Puffer" + +#. i18n: file recording-configuration-ui.ui line 580 +#: rc.cpp:102 rc.cpp:216 recording-configuration-ui.cpp:287 +#, no-c-format +msgid "Pre-Recordin&g" +msgstr "Aufnahme&vorlaufs" + +#. i18n: file recording-configuration-ui.ui line 610 +#: rc.cpp:105 rc.cpp:219 recording-configuration-ui.cpp:283 +#, no-c-format +msgid "E&nable" +msgstr "&Einschalten" + +#. i18n: file recording-configuration-ui.ui line 613 +#: rc.cpp:108 rc.cpp:222 recording-configuration-ui.cpp:284 +#, no-c-format +msgid "Alt+N" +msgstr "Alt+N" + +#. i18n: file recording-configuration-ui.ui line 651 +#: rc.cpp:111 rc.cpp:225 recording-configuration-ui.cpp:285 +#, no-c-format +msgid "PreRecording Time" +msgstr "Dauer des Aufnahmevorlaufs" + +#. i18n: file recording-configuration-ui.ui line 662 +#: rc.cpp:114 rc.cpp:228 recording-configuration-ui.cpp:286 +#, no-c-format +msgid " s" +msgstr " s" + +#: _translatorinfo.cpp:1 +msgid "" +"_: NAME OF TRANSLATORS\n" +"Your names" +msgstr "Ernst Martin Witte" + +#: _translatorinfo.cpp:3 +msgid "" +"_: EMAIL OF TRANSLATORS\n" +"Your emails" +msgstr "emw@nocabal.de" + +#: encoder_mp3.cpp:79 encoder_mp3.cpp:189 +msgid "Error %1 while encoding mp3. " +msgstr "Fehler %1 beim Codieren des MP3-Streams. " + +#: encoder_mp3.cpp:89 encoder_mp3.cpp:194 encoder_pcm.cpp:53 +msgid "Error %1 writing output. " +msgstr "Fehler %1 beim Schreiben der Ausgabedatei. " + +#: encoder_mp3.cpp:106 +msgid "Cannot initialize lalibmp3lame. " +msgstr "Die Funktionsbibliothek libmp3lame kann nicht initialisiert werden. " + +#: encoder_mp3.cpp:123 +msgid "Cannot initialize libmp3lame parameters. " +msgstr "" +"Die Parameter der Funktionsbibliothek libmp3lame konnten nicht initialisiert " +"werden." + +#: encoder_mp3.cpp:130 +msgid "Recorded by TDERadio" +msgstr "Aufzeichnung durch TDERadio" + +#: encoder_mp3.cpp:143 encoder_pcm.cpp:66 +msgid "Cannot open output file %1. " +msgstr "Die Ausgabedatei %1 kann nicht geöffnet werden. " + +#: encoder_mp3.cpp:156 +msgid "Cannot allocate buffers for mp3 encoding. " +msgstr "" +"Die Puffer für das Codieren des MP3-Streams konnten nicht angelegt werden. " + +#: encoder_ogg.cpp:94 +msgid "Failed writing data to ogg/vorbis output stream. " +msgstr "Das schreiben der Ogg/Vorbis-Daten schlug fehl. " + +#: encoder_ogg.cpp:136 +msgid "Cannot open Ogg/Vorbis output file %1. " +msgstr "Die Ogg/Vorbis-Ausgabedatei %1 konnte nicht geöffnet werden. " + +#: encoder_ogg.cpp:149 +msgid "Ogg/Vorbis Mode initialisation failed: invalid parameters for quality\n" +msgstr "" +"Die Initialisierung des Ogg/Vorbis-Modes schlug fehl: Ungültiger Qualitäts-" +"Parameter\n" + +#: encoder_ogg.cpp:200 +msgid "Failed writing Ogg/Vorbis header to output stream\n" +msgstr "Das Schreiben der Ogg/Vorbis-Kopfdaten der Ausgabedatei schlug fehl\n" + +#: recording-datamonitor.cpp:174 recording-datamonitor.cpp:179 +msgid "%1 dB" +msgstr "%1 dB" + +#: recording-monitor.cpp:34 +msgid "Recording Monitor" +msgstr "Aufnahme-Überwachung" + +#: recording-monitor.cpp:38 recording.cpp:47 +msgid "TDERadio Recording Monitor" +msgstr "TDERadio Aufnahme-Überwachung" + +#: recording-monitor.cpp:43 +msgid "SoundStream" +msgstr "Aufnahmedatenstrom" + +#: recording-monitor.cpp:45 +msgid "Status" +msgstr "Status" + +#: recording-monitor.cpp:46 recording-monitor.cpp:48 recording-monitor.cpp:50 +#: recording-monitor.cpp:52 recording-monitor.cpp:54 +msgid "<undefined>" +msgstr "<undefiniert>" + +#: recording-monitor.cpp:47 +msgid "Recording File" +msgstr "Aufnahmedatei" + +#: recording-monitor.cpp:49 +msgid "File Size" +msgstr "Dateigröße" + +#: recording-monitor.cpp:51 +msgid "Recording Time" +msgstr "Aufnahmezeit" + +#: recording-monitor.cpp:57 recording-monitor.cpp:393 +#: recording-monitor.cpp:396 +msgid "&Record" +msgstr "&Aufnehmen" + +#: recording-monitor.cpp:74 recording-monitor.cpp:118 +#: recording-monitor.cpp:141 +msgid "nothing" +msgstr "nichts" + +#: recording-monitor.cpp:339 +msgid "%1 Byte" +msgstr "%1 Byte" + +#: recording-monitor.cpp:340 +msgid "%1 kB" +msgstr "%1 kB" + +#: recording-monitor.cpp:341 +msgid "%1 MB" +msgstr "%1 MB" + +#: recording-monitor.cpp:342 +msgid "%1 GB" +msgstr "%1 GB" + +#: recording-monitor.cpp:345 +msgid "%1 Hz" +msgstr "%1 Hz" + +#: recording-monitor.cpp:393 +msgid "&Stop Recording" +msgstr "Aufnahme anhalten" + +#: recording.cpp:46 recording.cpp:54 +msgid "TDERadio Recording Plugin" +msgstr "TDERadio Aufnahme-Plugin" + +#: recording.cpp:131 recording.cpp:132 +msgid "Recording" +msgstr "Aufnahme" + +#: recording.cpp:360 +msgid "start capture not handled" +msgstr "Der Aufnahmestart wurde ignoriert" + +#: recording.cpp:367 +msgid "Recording starting" +msgstr "Die Aufnahme wird gestartet" + +#: recording.cpp:369 +msgid "starting encoding thread failed" +msgstr "Das Starten des Aufnahme-Threads schlug fehl" + +#: recording.cpp:451 +msgid "could not read suffient data" +msgstr "es konnten nicht ausreichend Daten gelesen werden" + +#: recording.cpp:482 +msgid "" +"Encoder input buffer overflow (buffer configuration problem?). Skipped %1 " +"input bytes" +msgstr "" +"Pufferüberlauf des Aufnahmepuffers des Kodierers/Komprimierers (Fehlerhafte " +"Konfiguration der Puffer?). Es wurden %1 Bytes ignoriert." + +#: recording.cpp:544 +msgid "Recording::outputFile: " +msgstr "Aufnahme::Ausgabedatei: " + +#: recording.cpp:595 +msgid "The encoding thread did not finish. It will be killed now." +msgstr "" +"Der Codierungs-Thread beendete sich nicht selber. Er wird jetzt mit roher " +"Gewalt beendet." + +#: recording.cpp:600 +msgid "Waiting for encoding thread to terminate." +msgstr "Warte auf die Beendigung des Codierungs-Threads." + +#: recording.cpp:619 +msgid "Recording stopped" +msgstr "Die Aufnahme wurde beendet" + +#: recording.cpp:652 +msgid "" +"Recording::notifySoundStreamData(encoded data): Receivers skipped %1 Bytes" +msgstr "" +"Recording::notifySoundStreamData(Kodierte Daten): Die Empfängermodule " +"übersprangen %1 Bytess" diff --git a/tderadio3/plugins/recording/po/ru.po b/tderadio3/plugins/recording/po/ru.po new file mode 100644 index 0000000..9713428 --- /dev/null +++ b/tderadio3/plugins/recording/po/ru.po @@ -0,0 +1,432 @@ +# translation of ru.po to +# translation of kradio-recording.po to +# This file is put in the public domain. +# Алексей Кузнецов <Alexey.Kouznetsov@GMail.com>, 2006. +# +msgid "" +msgstr "" +"Project-Id-Version: ru\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2006-11-11 18:43+0100\n" +"PO-Revision-Date: 2006-11-08 12:35+0300\n" +"Last-Translator: Алексей Кузнецов <Alexey.Kouznetsov@GMail.com>\n" +"Language-Team: <ru@li.org>\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"X-Generator: KBabel 1.10\n" + +#. i18n: file recording-configuration-ui.ui line 16 +#: rc.cpp:3 rc.cpp:117 recording-configuration-ui.cpp:244 +#, no-c-format +msgid "RecordingConfigurationUI" +msgstr "RecordingConfigurationUI" + +#. i18n: file recording-configuration-ui.ui line 34 +#: rc.cpp:6 rc.cpp:120 recording-configuration-ui.cpp:256 +#, no-c-format +msgid "Output" +msgstr "Выход" + +#. i18n: file recording-configuration-ui.ui line 138 +#: rc.cpp:9 rc.cpp:123 recording-configuration-ui.cpp:245 +#, no-c-format +msgid "MP3 Quality(0 - high, 9 - low)" +msgstr "Качество MP3 (0 — высокое, 9 — низкое)" + +#. i18n: file recording-configuration-ui.ui line 149 +#: rc.cpp:12 rc.cpp:126 recording-configuration-ui.cpp:247 +#, no-c-format +msgid "raw pcm output (.raw)" +msgstr "Неформатированный вывод (.raw)" + +#. i18n: file recording-configuration-ui.ui line 154 +#: rc.cpp:15 rc.cpp:129 recording-configuration-ui.cpp:248 +#, no-c-format +msgid "Microsoft Wave (.wav)" +msgstr "Microsoft Wave (.wav)" + +#. i18n: file recording-configuration-ui.ui line 159 +#: rc.cpp:18 rc.cpp:132 recording-configuration-ui.cpp:249 +#, no-c-format +msgid "Apple/SGI (.aiff)" +msgstr "Apple/SGI (.aiff)" + +#. i18n: file recording-configuration-ui.ui line 164 +#: rc.cpp:21 rc.cpp:135 recording-configuration-ui.cpp:250 +#, no-c-format +msgid "Sun/NeXT (.au)" +msgstr "Sun/NeXT (.au)" + +#. i18n: file recording-configuration-ui.ui line 169 +#: rc.cpp:24 rc.cpp:138 recording-configuration-ui.cpp:251 +#, no-c-format +msgid "MP3 Compressed (.mp3)" +msgstr "Сжатый MP3 (.mp3)" + +#. i18n: file recording-configuration-ui.ui line 174 +#: rc.cpp:27 rc.cpp:141 recording-configuration-ui.cpp:252 +#, no-c-format +msgid "Ogg/Vorbis Compressed (.ogg)" +msgstr "Сжатый Ogg Vorbis (.ogg)" + +#. i18n: file recording-configuration-ui.ui line 194 +#: rc.cpp:30 rc.cpp:144 recording-configuration-ui.cpp:253 +#, no-c-format +msgid "Recording Directory" +msgstr "Каталог для сохранения" + +#. i18n: file recording-configuration-ui.ui line 202 +#: rc.cpp:33 rc.cpp:147 recording-configuration-ui.cpp:254 +#, no-c-format +msgid "File Format" +msgstr "Формат файла" + +#. i18n: file recording-configuration-ui.ui line 210 +#: rc.cpp:36 rc.cpp:150 recording-configuration-ui.cpp:255 +#, no-c-format +msgid "Ogg Quality(0 - low, 9 - high)" +msgstr "Качество Ogg (0 — ниже, 9 — выше)" + +#. i18n: file recording-configuration-ui.ui line 239 +#: rc.cpp:39 rc.cpp:153 recording-configuration-ui.cpp:278 +#, no-c-format +msgid "I&nput" +msgstr "В&вод" + +#. i18n: file recording-configuration-ui.ui line 276 +#: rc.cpp:42 rc.cpp:156 recording-configuration-ui.cpp:258 +#, no-c-format +msgid "48000" +msgstr "48000" + +#. i18n: file recording-configuration-ui.ui line 281 +#: rc.cpp:45 rc.cpp:159 recording-configuration-ui.cpp:259 +#, no-c-format +msgid "44100" +msgstr "44100" + +#. i18n: file recording-configuration-ui.ui line 286 +#: rc.cpp:48 rc.cpp:162 recording-configuration-ui.cpp:260 +#, no-c-format +msgid "22050" +msgstr "22050" + +#. i18n: file recording-configuration-ui.ui line 291 +#: rc.cpp:51 rc.cpp:165 recording-configuration-ui.cpp:261 +#, no-c-format +msgid "11025" +msgstr "11025" + +#. i18n: file recording-configuration-ui.ui line 311 +#: rc.cpp:54 rc.cpp:168 recording-configuration-ui.cpp:262 +#, no-c-format +msgid "Endianess" +msgstr "Порядок байтов" + +#. i18n: file recording-configuration-ui.ui line 334 +#: rc.cpp:57 rc.cpp:171 recording-configuration-ui.cpp:264 +#, no-c-format +msgid "Stereo" +msgstr "2 (Стерео)" + +#. i18n: file recording-configuration-ui.ui line 339 +#: rc.cpp:60 rc.cpp:174 recording-configuration-ui.cpp:265 +#, no-c-format +msgid "Mono" +msgstr "1 (Моно)" + +#. i18n: file recording-configuration-ui.ui line 359 +#: rc.cpp:63 rc.cpp:177 recording-configuration-ui.cpp:266 +#: recording-monitor.cpp:53 +#, no-c-format +msgid "Sample Rate" +msgstr "Частота дискретизации" + +#. i18n: file recording-configuration-ui.ui line 365 +#: rc.cpp:66 rc.cpp:180 recording-configuration-ui.cpp:268 +#, no-c-format +msgid "Little Endian" +msgstr "Little Endian" + +#. i18n: file recording-configuration-ui.ui line 370 +#: rc.cpp:69 rc.cpp:183 recording-configuration-ui.cpp:269 +#, fuzzy, no-c-format +msgid "Big Endian" +msgstr "Big Endian" + +#. i18n: file recording-configuration-ui.ui line 388 +#: rc.cpp:72 rc.cpp:186 recording-configuration-ui.cpp:271 +#, no-c-format +msgid "16" +msgstr "16" + +#. i18n: file recording-configuration-ui.ui line 393 +#: rc.cpp:75 rc.cpp:189 recording-configuration-ui.cpp:272 +#, no-c-format +msgid "8" +msgstr "8" + +#. i18n: file recording-configuration-ui.ui line 413 +#: rc.cpp:78 rc.cpp:192 recording-configuration-ui.cpp:273 +#, no-c-format +msgid "Channels" +msgstr "Число каналов" + +#. i18n: file recording-configuration-ui.ui line 421 +#: rc.cpp:81 rc.cpp:195 recording-configuration-ui.cpp:274 +#, no-c-format +msgid "Sample Bits" +msgstr "Бит на элемент выборки" + +#. i18n: file recording-configuration-ui.ui line 427 +#: rc.cpp:84 rc.cpp:198 recording-configuration-ui.cpp:276 +#, no-c-format +msgid "Signed" +msgstr "Со знаком" + +#. i18n: file recording-configuration-ui.ui line 432 +#: rc.cpp:87 rc.cpp:201 recording-configuration-ui.cpp:277 +#, no-c-format +msgid "Unsigned" +msgstr "Без знака" + +#. i18n: file recording-configuration-ui.ui line 490 +#: rc.cpp:90 rc.cpp:204 recording-configuration-ui.cpp:282 +#, no-c-format +msgid "&Buffers" +msgstr "&Буферы" + +#. i18n: file recording-configuration-ui.ui line 512 +#: rc.cpp:93 rc.cpp:207 recording-configuration-ui.cpp:279 +#, no-c-format +msgid " kB" +msgstr " кБ" + +#. i18n: file recording-configuration-ui.ui line 532 +#: rc.cpp:96 rc.cpp:210 recording-configuration-ui.cpp:280 +#, no-c-format +msgid "Encoding Buffer Size" +msgstr "Размер буфера для записи" + +#. i18n: file recording-configuration-ui.ui line 551 +#: rc.cpp:99 rc.cpp:213 recording-configuration-ui.cpp:281 +#, no-c-format +msgid "Number of Buffers" +msgstr "Количество буферов" + +#. i18n: file recording-configuration-ui.ui line 580 +#: rc.cpp:102 rc.cpp:216 recording-configuration-ui.cpp:287 +#, no-c-format +msgid "Pre-Recordin&g" +msgstr "&Упреждающая запись" + +#. i18n: file recording-configuration-ui.ui line 610 +#: rc.cpp:105 rc.cpp:219 recording-configuration-ui.cpp:283 +#, no-c-format +msgid "E&nable" +msgstr "Включить" + +#. i18n: file recording-configuration-ui.ui line 613 +#: rc.cpp:108 rc.cpp:222 recording-configuration-ui.cpp:284 +#, no-c-format +msgid "Alt+N" +msgstr "Alt+N" + +#. i18n: file recording-configuration-ui.ui line 651 +#: rc.cpp:111 rc.cpp:225 recording-configuration-ui.cpp:285 +#, no-c-format +msgid "PreRecording Time" +msgstr "Упреждение" + +#. i18n: file recording-configuration-ui.ui line 662 +#: rc.cpp:114 rc.cpp:228 recording-configuration-ui.cpp:286 +#, no-c-format +msgid " s" +msgstr " с" + +#: _translatorinfo.cpp:1 +msgid "" +"_: NAME OF TRANSLATORS\n" +"Your names" +msgstr "Алексей Кузнецов" + +#: _translatorinfo.cpp:3 +msgid "" +"_: EMAIL OF TRANSLATORS\n" +"Your emails" +msgstr "Alexey.Kouznetsov@GMail.com" + +#: encoder_mp3.cpp:79 encoder_mp3.cpp:189 +msgid "Error %1 while encoding mp3. " +msgstr "При кодировании MP3 произошла ошибка: %1. " + +#: encoder_mp3.cpp:89 encoder_mp3.cpp:194 encoder_pcm.cpp:53 +msgid "Error %1 writing output. " +msgstr "Во время записи произошла ошибка: %1" + +#: encoder_mp3.cpp:106 +msgid "Cannot initialize lalibmp3lame. " +msgstr "" +"Не могу инициализировать lalibmp3lame — необходимо для кодирования в MP3." + +#: encoder_mp3.cpp:123 +msgid "Cannot initialize libmp3lame parameters. " +msgstr "" +"Не могу инициализировать параметры libmp3lame — необходимо для кодирования в " +"MP3." + +#: encoder_mp3.cpp:130 +msgid "Recorded by TDERadio" +msgstr "Запись TDERadio" + +#: encoder_mp3.cpp:143 encoder_pcm.cpp:66 +msgid "Cannot open output file %1. " +msgstr "Не могу открыть выходной файл %1. " + +#: encoder_mp3.cpp:156 +msgid "Cannot allocate buffers for mp3 encoding. " +msgstr "Не могу разместить в памяти буферы для кодирования MP3." + +#: encoder_ogg.cpp:94 +msgid "Failed writing data to ogg/vorbis output stream. " +msgstr "Ошибка записи данных в выходной поток ogg/vorbis. " + +#: encoder_ogg.cpp:136 +msgid "Cannot open Ogg/Vorbis output file %1. " +msgstr "Не могу открыть выходной файл Ogg/Vorbis \"%1\". " + +#: encoder_ogg.cpp:149 +msgid "Ogg/Vorbis Mode initialisation failed: invalid parameters for quality\n" +msgstr "" +"Инициализация режима Ogg/Vorbis не удалась: качество записи указано неверно\n" + +#: encoder_ogg.cpp:200 +msgid "Failed writing Ogg/Vorbis header to output stream\n" +msgstr "Ошибка записи заголовка Ogg/Vorbis в выходной поток\n" + +#: recording-datamonitor.cpp:174 recording-datamonitor.cpp:179 +msgid "%1 dB" +msgstr "%1 дБ" + +#: recording-monitor.cpp:34 +msgid "Recording Monitor" +msgstr "Монитор записи" + +#: recording-monitor.cpp:38 recording.cpp:47 +msgid "TDERadio Recording Monitor" +msgstr "Монитор записи для TDERadio" + +#: recording-monitor.cpp:43 +msgid "SoundStream" +msgstr "Источник" + +#: recording-monitor.cpp:45 +msgid "Status" +msgstr "Состояние" + +#: recording-monitor.cpp:46 recording-monitor.cpp:48 recording-monitor.cpp:50 +#: recording-monitor.cpp:52 recording-monitor.cpp:54 +msgid "<undefined>" +msgstr "<не определено>" + +#: recording-monitor.cpp:47 +msgid "Recording File" +msgstr "Файл" + +#: recording-monitor.cpp:49 +msgid "File Size" +msgstr "Размер файла" + +#: recording-monitor.cpp:51 +msgid "Recording Time" +msgstr "Длительность записи" + +#: recording-monitor.cpp:57 recording-monitor.cpp:393 +#: recording-monitor.cpp:396 +msgid "&Record" +msgstr "&Начать запись" + +#: recording-monitor.cpp:74 recording-monitor.cpp:118 +#: recording-monitor.cpp:141 +msgid "nothing" +msgstr "(нет)" + +#: recording-monitor.cpp:339 +msgid "%1 Byte" +msgstr "%1 байт" + +#: recording-monitor.cpp:340 +msgid "%1 kB" +msgstr "%1 кБ" + +#: recording-monitor.cpp:341 +msgid "%1 MB" +msgstr "%1 МБ" + +#: recording-monitor.cpp:342 +msgid "%1 GB" +msgstr "%1 ГБ" + +#: recording-monitor.cpp:345 +msgid "%1 Hz" +msgstr "%1 Гц" + +#: recording-monitor.cpp:393 +msgid "&Stop Recording" +msgstr "&Остановить запись" + +#: recording.cpp:46 recording.cpp:54 +msgid "TDERadio Recording Plugin" +msgstr "Модуль записи звука для TDERadio" + +#: recording.cpp:131 recording.cpp:132 +msgid "Recording" +msgstr "Запись" + +#: recording.cpp:360 +msgid "start capture not handled" +msgstr "" + +#: recording.cpp:367 +msgid "Recording starting" +msgstr "Запись запущена" + +#: recording.cpp:369 +msgid "starting encoding thread failed" +msgstr "Не смог запустить процесс кодирования" + +#: recording.cpp:451 +msgid "could not read suffient data" +msgstr "Не смог прочесть достаточно данных" + +#: recording.cpp:482 +msgid "" +"Encoder input buffer overflow (buffer configuration problem?). Skipped %1 " +"input bytes" +msgstr "" +"Переполнение на входе кодировщика (вероятно, неправильно настроен буфер). " +"Пропускаю %1 байт на входе." + +#: recording.cpp:544 +msgid "Recording::outputFile: " +msgstr "Recording::outputFile (выходной файл записи):" + +#: recording.cpp:595 +msgid "The encoding thread did not finish. It will be killed now." +msgstr "Нить кодировщика не завершилась. Процесс будет уничтожен." + +#: recording.cpp:600 +msgid "Waiting for encoding thread to terminate." +msgstr "Жду завершения нити кодировщика" + +#: recording.cpp:619 +#, fuzzy +msgid "Recording stopped" +msgstr "Запись заершена" + +#: recording.cpp:652 +msgid "" +"Recording::notifySoundStreamData(encoded data): Receivers skipped %1 Bytes" +msgstr "" +"Recording::notifySoundStreamData(encoded data): Приёмник пропустил %1 байт" diff --git a/tderadio3/plugins/recording/reccfg_interfaces.cpp b/tderadio3/plugins/recording/reccfg_interfaces.cpp new file mode 100644 index 0000000..9cbc9e6 --- /dev/null +++ b/tderadio3/plugins/recording/reccfg_interfaces.cpp @@ -0,0 +1,151 @@ +/*************************************************************************** + reccfg_interfaces.cpp - description + ------------------- + begin : Sun May 01 2005 + copyright : (C) 2005by Martin Witte + email : witte@kawo1.rwth-aachen.de + ***************************************************************************/ + +/*************************************************************************** + * * + * 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 <linux/soundcard.h> +#include "reccfg_interfaces.h" + +// IRecCfg + +IF_IMPL_SENDER ( IRecCfg::notifyEncoderBufferChanged (size_t BufferSize, size_t BufferCount), + noticeEncoderBufferChanged(BufferSize, BufferCount) + ); +IF_IMPL_SENDER ( IRecCfg::notifySoundFormatChanged(const SoundFormat &sf), + noticeSoundFormatChanged(sf) + ); +IF_IMPL_SENDER ( IRecCfg::notifyMP3QualityChanged(int q), + noticeMP3QualityChanged(q) + ); +IF_IMPL_SENDER ( IRecCfg::notifyOggQualityChanged(float q), + noticeOggQualityChanged(q) + ); +IF_IMPL_SENDER ( IRecCfg::notifyRecordingDirectoryChanged(const TQString &dir), + noticeRecordingDirectoryChanged(dir) + ); +IF_IMPL_SENDER ( IRecCfg::notifyOutputFormatChanged(RecordingConfig::OutputFormat of), + noticeOutputFormatChanged(of) + ); +IF_IMPL_SENDER ( IRecCfg::notifyPreRecordingChanged(bool enable, int seconds), + noticePreRecordingChanged(enable, seconds) + ); +IF_IMPL_SENDER ( IRecCfg::notifyRecordingConfigChanged (const RecordingConfig &cfg), + noticeRecordingConfigChanged(cfg) + ); + +// IRecCfgClient + +IF_IMPL_SENDER ( IRecCfgClient::sendEncoderBuffer (size_t BufferSize, size_t BufferCount), + setEncoderBuffer(BufferSize, BufferCount) + ); +IF_IMPL_SENDER ( IRecCfgClient::sendSoundFormat(const SoundFormat &sf), + setSoundFormat(sf) + ); +IF_IMPL_SENDER ( IRecCfgClient::sendMP3Quality(int q), + setMP3Quality(q) + ); +IF_IMPL_SENDER ( IRecCfgClient::sendOggQuality(float q), + setOggQuality(q) + ); +IF_IMPL_SENDER ( IRecCfgClient::sendRecordingDirectory(const TQString &dir), + setRecordingDirectory(dir) + ); +IF_IMPL_SENDER ( IRecCfgClient::sendOutputFormat(RecordingConfig::OutputFormat of), + setOutputFormat(of) + ); +IF_IMPL_SENDER ( IRecCfgClient::sendPreRecording(bool enable, int seconds), + setPreRecording(enable, seconds) + ); +IF_IMPL_SENDER ( IRecCfgClient::sendRecordingConfig(const RecordingConfig &cfg), + setRecordingConfig(cfg) + ); + +IF_IMPL_QUERY ( void IRecCfgClient::queryEncoderBuffer(size_t &BufferSize, size_t &BufferCount), + getEncoderBuffer(BufferSize, BufferCount), + + ); + +static SoundFormat defaultSoundFormat; +IF_IMPL_QUERY ( const SoundFormat &IRecCfgClient::querySoundFormat (), + getSoundFormat(), + defaultSoundFormat + ); + +IF_IMPL_QUERY ( int IRecCfgClient::queryMP3Quality (), + getMP3Quality(), + 7 + ); + +IF_IMPL_QUERY ( float IRecCfgClient::queryOggQuality (), + getOggQuality(), + 7 + ); + +static TQString defaultRecDir("/tmp"); +IF_IMPL_QUERY ( const TQString &IRecCfgClient::queryRecordingDirectory(), + getRecordingDirectory(), + defaultRecDir + ); + +IF_IMPL_QUERY ( RecordingConfig::OutputFormat IRecCfgClient::queryOutputFormat(), + getOutputFormat(), + RecordingConfig::outputWAV + ); + +IF_IMPL_QUERY ( bool IRecCfgClient::queryPreRecording(int &seconds), + getPreRecording(seconds), + false + ); + +static RecordingConfig defaultRecConfig; +IF_IMPL_QUERY ( const RecordingConfig &IRecCfgClient::queryRecordingConfig(), + getRecordingConfig(), + defaultRecConfig + ); + +void IRecCfgClient::noticeConnectedI (cmplInterface *, bool /*pointer_valid*/) +{ + size_t bs = 0, bc = 0; + queryEncoderBuffer(bs, bc); + noticeEncoderBufferChanged(bs, bc); + noticeSoundFormatChanged(querySoundFormat()); + noticeMP3QualityChanged (queryMP3Quality()); + noticeOggQualityChanged (queryOggQuality()); + noticeRecordingDirectoryChanged(queryRecordingDirectory()); + noticeOutputFormatChanged(queryOutputFormat()); + int s = 0; + bool e = queryPreRecording(s); + noticePreRecordingChanged(e, s); + noticeRecordingConfigChanged(queryRecordingConfig()); +} + + +void IRecCfgClient::noticeDisconnectedI (cmplInterface *, bool /*pointer_valid*/) +{ + size_t bs = 0, bc = 0; + queryEncoderBuffer(bs, bc); + noticeEncoderBufferChanged(bs, bc); + noticeSoundFormatChanged(querySoundFormat()); + noticeMP3QualityChanged (queryMP3Quality()); + noticeOggQualityChanged (queryOggQuality()); + noticeRecordingDirectoryChanged(queryRecordingDirectory()); + noticeOutputFormatChanged(queryOutputFormat()); + int s = 0; + bool e = queryPreRecording(s); + noticePreRecordingChanged(e, s); + noticeRecordingConfigChanged(queryRecordingConfig()); +} + + diff --git a/tderadio3/plugins/recording/reccfg_interfaces.h b/tderadio3/plugins/recording/reccfg_interfaces.h new file mode 100644 index 0000000..937ca42 --- /dev/null +++ b/tderadio3/plugins/recording/reccfg_interfaces.h @@ -0,0 +1,102 @@ +/*************************************************************************** + reccfg_interfaces.h - description + ------------------- + begin : Sun May 01 2005 + copyright : (C) 2005 by Martin Witte + email : witte@kawo1.rwth-aachen.de + ***************************************************************************/ + +/*************************************************************************** + * * + * 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. * + * * + ***************************************************************************/ + +#ifndef KRADIO_RECCFG_INTERFACES_H +#define KRADIO_RECCFG_INTERFACES_H + +#include "../../src/include/interfaces.h" +#include "recording-config.h" + +INTERFACE(IRecCfg, IRecCfgClient) +{ +public: + IF_CON_DESTRUCTOR(IRecCfg, -1) + +RECEIVERS: + IF_RECEIVER( setEncoderBuffer (size_t BufferSize, size_t BufferCount) ) + IF_RECEIVER( setSoundFormat (const SoundFormat &sf) ) + IF_RECEIVER( setMP3Quality (int q) ) + IF_RECEIVER( setOggQuality (float q) ) + IF_RECEIVER( setRecordingDirectory(const TQString &dir) ) + IF_RECEIVER( setOutputFormat (RecordingConfig::OutputFormat of) ) + IF_RECEIVER( setPreRecording (bool enable, int seconds) ) + IF_RECEIVER( setRecordingConfig (const RecordingConfig &cfg) ) + +SENDERS: + IF_SENDER ( notifyEncoderBufferChanged (size_t BufferSize, size_t BufferCount) ) + IF_SENDER ( notifySoundFormatChanged (const SoundFormat &sf) ) + IF_SENDER ( notifyMP3QualityChanged (int q) ) + IF_SENDER ( notifyOggQualityChanged (float q) ) + IF_SENDER ( notifyRecordingDirectoryChanged(const TQString &dir) ) + IF_SENDER ( notifyOutputFormatChanged (RecordingConfig::OutputFormat of) ) + IF_SENDER ( notifyPreRecordingChanged (bool enable, int seconds) ) + IF_SENDER ( notifyRecordingConfigChanged (const RecordingConfig &cfg) ) + +ANSWERS: + IF_ANSWER ( void getEncoderBuffer(size_t &BufferSize, size_t &BufferCount) const ) + IF_ANSWER ( const SoundFormat &getSoundFormat () const ) + IF_ANSWER ( int getMP3Quality () const ) + IF_ANSWER ( float getOggQuality () const ) + IF_ANSWER ( const TQString &getRecordingDirectory() const ) + IF_ANSWER ( RecordingConfig::OutputFormat getOutputFormat() const ) + IF_ANSWER ( bool getPreRecording(int &seconds) const ) + IF_ANSWER ( const RecordingConfig &getRecordingConfig() const ) +}; + + + +INTERFACE(IRecCfgClient, IRecCfg) +{ +public: + IF_CON_DESTRUCTOR(IRecCfgClient, 1) + +SENDERS: + IF_SENDER ( sendEncoderBuffer (size_t BufferSize, size_t BufferCount) ) + IF_SENDER ( sendSoundFormat (const SoundFormat &sf) ) + IF_SENDER ( sendMP3Quality (int q) ) + IF_SENDER ( sendOggQuality (float q) ) + IF_SENDER ( sendRecordingDirectory(const TQString &dir) ) + IF_SENDER ( sendOutputFormat (RecordingConfig::OutputFormat of) ) + IF_SENDER ( sendPreRecording (bool enable, int seconds) ) + IF_SENDER ( sendRecordingConfig (const RecordingConfig &cfg) ) + +RECEIVERS: + IF_RECEIVER( noticeEncoderBufferChanged (size_t BufferSize, size_t BufferCount) ) + IF_RECEIVER( noticeSoundFormatChanged (const SoundFormat &sf) ) + IF_RECEIVER( noticeMP3QualityChanged (int q) ) + IF_RECEIVER( noticeOggQualityChanged (float q) ) + IF_RECEIVER( noticeRecordingDirectoryChanged(const TQString &dir) ) + IF_RECEIVER( noticeOutputFormatChanged (RecordingConfig::OutputFormat of) ) + IF_RECEIVER( noticePreRecordingChanged (bool enable, int seconds) ) + IF_RECEIVER( noticeRecordingConfigChanged (const RecordingConfig &cfg) ) + +QUERIES: + IF_QUERY ( void queryEncoderBuffer(size_t &BufferSize, size_t &BufferCount) ) + IF_QUERY ( const SoundFormat &querySoundFormat () ) + IF_QUERY ( int queryMP3Quality () ) + IF_QUERY ( float queryOggQuality () ) + IF_QUERY ( const TQString &queryRecordingDirectory() ) + IF_QUERY ( RecordingConfig::OutputFormat queryOutputFormat() ) + IF_QUERY ( bool queryPreRecording(int &seconds) ) + IF_QUERY ( const RecordingConfig &queryRecordingConfig() ) + +RECEIVERS: + virtual void noticeConnectedI (cmplInterface *, bool /*pointer_valid*/); + virtual void noticeDisconnectedI (cmplInterface *, bool /*pointer_valid*/); +}; + +#endif diff --git a/tderadio3/plugins/recording/recording-config.cpp b/tderadio3/plugins/recording/recording-config.cpp new file mode 100644 index 0000000..2395e28 --- /dev/null +++ b/tderadio3/plugins/recording/recording-config.cpp @@ -0,0 +1,215 @@ +/*************************************************************************** + recording-config.cpp - description + ------------------- + begin : Mi Apr 30 2005 + copyright : (C) 2005 by Martin Witte + email : witte@kawo1.rwth-aachen.de + ***************************************************************************/ + +/*************************************************************************** + * * + * 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 "recording-config.h" + +#include <sndfile.h> + +RecordingConfig::RecordingConfig () +: m_EncodeBufferSize(256*1024), + m_EncodeBufferCount(3), + m_mp3Quality(7), + m_oggQuality(1.0), + m_Directory("/tmp"), + m_OutputFormat(outputWAV), + m_PreRecordingEnable (false), + m_PreRecordingSeconds(10) +{ + checkFormatSettings(); +} + +RecordingConfig::RecordingConfig (const TQString &directory, + OutputFormat of, + const SoundFormat &sf, int mp3_q, float ogg_q) +: m_EncodeBufferSize(256*1024), + m_EncodeBufferCount(3), + m_SoundFormat(sf), + m_mp3Quality(mp3_q), + m_oggQuality(ogg_q), + m_Directory(directory), + m_OutputFormat(of), + m_PreRecordingEnable (false), + m_PreRecordingSeconds(10) +{ + checkFormatSettings(); +} + + +RecordingConfig::RecordingConfig (const RecordingConfig &c) + : + m_EncodeBufferSize(c.m_EncodeBufferSize), + m_EncodeBufferCount(c.m_EncodeBufferCount), + m_SoundFormat(c.m_SoundFormat), + m_mp3Quality(c.m_mp3Quality), + m_oggQuality(c.m_oggQuality), + m_Directory(c.m_Directory), + m_OutputFormat(c.m_OutputFormat), + m_PreRecordingEnable (false), + m_PreRecordingSeconds(10) +{ + checkFormatSettings(); +} + + +void RecordingConfig::restoreConfig(TDEConfig *c) +{ + m_EncodeBufferSize = c->readNumEntry("encodeBufferSize", 256*1024); + m_EncodeBufferCount = c->readNumEntry("encodeBufferCount", 3); + + m_SoundFormat.restoreConfig("", c); + m_Directory = c->readEntry("directory", "/tmp"); + m_mp3Quality = c->readNumEntry("mp3quality", 7); + m_oggQuality = c->readDoubleNumEntry("oggquality", 1.0); + TQString of = c->readEntry("outputFormat", ".wav"); + + if (of == ".wav") + m_OutputFormat = outputWAV; + else if (of == ".aiff") + m_OutputFormat = outputAIFF; + else if (of == ".au") + m_OutputFormat = outputAU; +#ifdef HAVE_LAME + else if (of == ".mp3") + m_OutputFormat = outputMP3; +#endif +#ifdef HAVE_OGG + else if (of == ".ogg") + m_OutputFormat = outputOGG; +#endif + else if (of == ".raw") + m_OutputFormat = outputRAW; + + // if there was any unknown format + else + m_OutputFormat = outputWAV; + + m_PreRecordingEnable = c->readBoolEntry("prerecording-enable", false); + m_PreRecordingSeconds = c->readNumEntry("prerecording-seconds", 10); + + checkFormatSettings(); +} + + +void RecordingConfig::saveConfig(TDEConfig *c) const +{ + c->writeEntry("encodeBufferSize", m_EncodeBufferSize); + c->writeEntry("encodeBufferCount", m_EncodeBufferCount); + m_SoundFormat.saveConfig("", c); + c->writeEntry("directory", m_Directory); + c->writeEntry("mp3quality", m_mp3Quality); + c->writeEntry("oggquality", m_oggQuality); + + switch(m_OutputFormat) { + case outputWAV: c->writeEntry("outputFormat", ".wav"); break; + case outputAIFF: c->writeEntry("outputFormat", ".aiff"); break; + case outputAU: c->writeEntry("outputFormat", ".au"); break; + case outputMP3: c->writeEntry("outputFormat", ".mp3"); break; + case outputOGG: c->writeEntry("outputFormat", ".ogg"); break; + case outputRAW: c->writeEntry("outputFormat", ".raw"); break; + default: c->writeEntry("outputFormat", ".wav"); break; + } + + c->writeEntry("prerecording-enable", m_PreRecordingEnable); + c->writeEntry("prerecording-seconds", m_PreRecordingSeconds); +} + + +void RecordingConfig::getSoundFileInfo(SF_INFO &sinfo, bool input) +{ + checkFormatSettings(); + + sinfo.samplerate = m_SoundFormat.m_SampleRate; + sinfo.channels = m_SoundFormat.m_Channels; + sinfo.format = 0; + sinfo.seekable = !input; + + // U8 only supported for RAW and WAV + if (m_SoundFormat.m_SampleBits == 8) { + if ((m_SoundFormat.m_IsSigned && + m_OutputFormat != outputWAV) || + m_OutputFormat == outputAU + ) { + sinfo.format |= SF_FORMAT_PCM_S8; + } else { + sinfo.format |= SF_FORMAT_PCM_U8; + } + } + if (m_SoundFormat.m_SampleBits == 16) + sinfo.format |= SF_FORMAT_PCM_16; + + if (m_SoundFormat.m_Endianess == LITTLE_ENDIAN) + sinfo.format |= SF_ENDIAN_LITTLE; + else + sinfo.format |= SF_ENDIAN_BIG; + + if (input) { + sinfo.format |= SF_FORMAT_RAW; + } else { + switch (m_OutputFormat) { + case outputWAV: sinfo.format |= SF_FORMAT_WAV; break; + case outputAIFF: sinfo.format |= SF_FORMAT_AIFF; break; + case outputAU: sinfo.format |= SF_FORMAT_AU; break; + case outputRAW: sinfo.format |= SF_FORMAT_RAW; break; + default: sinfo.format |= SF_FORMAT_WAV; break; + } + } +} + + +void RecordingConfig::checkFormatSettings() +{ + // correct Endianess and Signs for specific formats + switch (m_OutputFormat) { + case outputWAV: + m_SoundFormat.m_Endianess = LITTLE_ENDIAN; + if (m_SoundFormat.m_SampleBits == 8) + m_SoundFormat.m_IsSigned = false; + // libsndfile only supports signed 16 bit samples + if (m_SoundFormat.m_SampleBits == 16) + m_SoundFormat.m_IsSigned = true; + break; + case outputAIFF: + m_SoundFormat.m_Endianess = BIG_ENDIAN; + // libsndfile only supports signed 16 bit samples + if (m_SoundFormat.m_SampleBits == 16) + m_SoundFormat.m_IsSigned = true; + break; + case outputAU: + m_SoundFormat.m_Endianess = BIG_ENDIAN; + m_SoundFormat.m_IsSigned = true; + // libsndfile only supports signed 16 bit samples + if (m_SoundFormat.m_SampleBits == 16) + m_SoundFormat.m_IsSigned = true; + break; + case outputMP3: + m_SoundFormat.m_IsSigned = true; + m_SoundFormat.m_SampleBits = 16; + break; + case outputOGG: + m_SoundFormat.m_IsSigned = true; + m_SoundFormat.m_SampleBits = 16; + break; + case outputRAW: + // libsndfile only supports signed 16 bit samples + if (m_SoundFormat.m_SampleBits == 16) + m_SoundFormat.m_IsSigned = true; + break; + default: + break; + } +} + diff --git a/tderadio3/plugins/recording/recording-config.h b/tderadio3/plugins/recording/recording-config.h new file mode 100644 index 0000000..ba7ba52 --- /dev/null +++ b/tderadio3/plugins/recording/recording-config.h @@ -0,0 +1,73 @@ +/*************************************************************************** + recording-config.h - description + ------------------- + begin : Mi Apr 30 2005 + copyright : (C) 2005 by Martin Witte + email : witte@kawo1.rwth-aachen.de + ***************************************************************************/ + +/*************************************************************************** + * * + * 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. * + * * + ***************************************************************************/ + +#ifndef KRADIO_RECORDING_CONFIG_H +#define KRADIO_RECORDING_CONFIG_H + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include "../../src/include/soundformat.h" + +class TDEConfig; +struct SF_INFO; + +class RecordingConfig +{ +public: + enum OutputFormat { + outputWAV, + outputAIFF, + outputAU, + outputMP3, + outputOGG, + outputRAW + }; + +public: + RecordingConfig (); + RecordingConfig (const TQString &directory, + OutputFormat of, + const SoundFormat &, int mp3_q, float ogg_q); + RecordingConfig (const RecordingConfig &c); + + void restoreConfig(TDEConfig *c); + void saveConfig(TDEConfig *c) const; + + void getSoundFileInfo(SF_INFO &info, bool input); + + void checkFormatSettings(); + +public: + size_t m_EncodeBufferSize; + size_t m_EncodeBufferCount; + + SoundFormat m_SoundFormat; + int m_mp3Quality; + float m_oggQuality; + TQString m_Directory; + OutputFormat m_OutputFormat; + + bool m_PreRecordingEnable; + int m_PreRecordingSeconds; +}; + + + + +#endif diff --git a/tderadio3/plugins/recording/recording-configuration-ui.ui b/tderadio3/plugins/recording/recording-configuration-ui.ui new file mode 100644 index 0000000..2a90973 --- /dev/null +++ b/tderadio3/plugins/recording/recording-configuration-ui.ui @@ -0,0 +1,731 @@ +<!DOCTYPE UI><UI version="3.3" stdsetdef="1"> +<class>RecordingConfigurationUI</class> +<widget class="TQWidget"> + <property name="name"> + <cstring>RecordingConfigurationUI</cstring> + </property> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>468</width> + <height>197</height> + </rect> + </property> + <property name="caption"> + <string>RecordingConfigurationUI</string> + </property> + <grid> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <property name="margin"> + <number>0</number> + </property> + <widget class="TQTabWidget" row="0" column="0"> + <property name="name"> + <cstring>kTabWidget13</cstring> + </property> + <widget class="TQWidget"> + <property name="name"> + <cstring>TabPage_2</cstring> + </property> + <attribute name="title"> + <string>Output</string> + </attribute> + <grid> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <property name="margin"> + <number>11</number> + </property> + <widget class="TQLayoutWidget" row="0" column="0"> + <property name="name"> + <cstring>layout7</cstring> + </property> + <grid> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="TQLayoutWidget" row="2" column="1" rowspan="1" colspan="2"> + <property name="name"> + <cstring>layout3_2</cstring> + </property> + <hbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <spacer> + <property name="name"> + <cstring>spacer5_2</cstring> + </property> + <property name="orientation"> + <enum>Horizontal</enum> + </property> + <property name="sizeType"> + <enum>Expanding</enum> + </property> + <property name="sizeHint"> + <size> + <width>141</width> + <height>20</height> + </size> + </property> + </spacer> + <widget class="TQSpinBox"> + <property name="name"> + <cstring>editOggQuality</cstring> + </property> + <property name="maxValue"> + <number>9</number> + </property> + <property name="minValue"> + <number>0</number> + </property> + <property name="value"> + <number>7</number> + </property> + </widget> + </hbox> + </widget> + <widget class="TQLayoutWidget" row="1" column="2"> + <property name="name"> + <cstring>layout3</cstring> + </property> + <hbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <spacer> + <property name="name"> + <cstring>spacer5</cstring> + </property> + <property name="orientation"> + <enum>Horizontal</enum> + </property> + <property name="sizeType"> + <enum>Expanding</enum> + </property> + <property name="sizeHint"> + <size> + <width>141</width> + <height>20</height> + </size> + </property> + </spacer> + <widget class="TQSpinBox"> + <property name="name"> + <cstring>editMP3Quality</cstring> + </property> + <property name="maxValue"> + <number>9</number> + </property> + <property name="minValue"> + <number>0</number> + </property> + <property name="value"> + <number>5</number> + </property> + </widget> + </hbox> + </widget> + <widget class="TQLabel" row="1" column="0" rowspan="1" colspan="2"> + <property name="name"> + <cstring>labelMP3Quality</cstring> + </property> + <property name="text"> + <string>MP3 Quality(0 - high, 9 - low)</string> + </property> + </widget> + <widget class="KURLRequester" row="3" column="2"> + <property name="name"> + <cstring>editDirectory</cstring> + </property> + </widget> + <widget class="KComboBox" row="0" column="2"> + <item> + <property name="text"> + <string>raw pcm output (.raw)</string> + </property> + </item> + <item> + <property name="text"> + <string>Microsoft Wave (.wav)</string> + </property> + </item> + <item> + <property name="text"> + <string>Apple/SGI (.aiff)</string> + </property> + </item> + <item> + <property name="text"> + <string>Sun/NeXT (.au)</string> + </property> + </item> + <item> + <property name="text"> + <string>MP3 Compressed (.mp3)</string> + </property> + </item> + <item> + <property name="text"> + <string>Ogg/Vorbis Compressed (.ogg)</string> + </property> + </item> + <property name="name"> + <cstring>editFileFormat</cstring> + </property> + <property name="sizePolicy"> + <sizepolicy> + <hsizetype>7</hsizetype> + <vsizetype>0</vsizetype> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + </widget> + <widget class="TQLabel" row="3" column="0" rowspan="1" colspan="2"> + <property name="name"> + <cstring>lableDirectory</cstring> + </property> + <property name="text"> + <string>Recording Directory</string> + </property> + </widget> + <widget class="TQLabel" row="0" column="0" rowspan="1" colspan="2"> + <property name="name"> + <cstring>lableFileFormat</cstring> + </property> + <property name="text"> + <string>File Format</string> + </property> + </widget> + <widget class="TQLabel" row="2" column="0"> + <property name="name"> + <cstring>labelOggQuality</cstring> + </property> + <property name="text"> + <string>Ogg Quality(0 - low, 9 - high)</string> + </property> + </widget> + </grid> + </widget> + <spacer row="1" column="0"> + <property name="name"> + <cstring>spacer132</cstring> + </property> + <property name="orientation"> + <enum>Vertical</enum> + </property> + <property name="sizeType"> + <enum>Expanding</enum> + </property> + <property name="sizeHint"> + <size> + <width>20</width> + <height>5</height> + </size> + </property> + </spacer> + </grid> + </widget> + <widget class="TQWidget"> + <property name="name"> + <cstring>TabPage</cstring> + </property> + <attribute name="title"> + <string>I&nput</string> + </attribute> + <grid> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <property name="margin"> + <number>11</number> + </property> + <widget class="TQLayoutWidget" row="0" column="0"> + <property name="name"> + <cstring>layout69</cstring> + </property> + <grid> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <spacer row="0" column="2"> + <property name="name"> + <cstring>spacer1</cstring> + </property> + <property name="orientation"> + <enum>Horizontal</enum> + </property> + <property name="sizeType"> + <enum>Minimum</enum> + </property> + <property name="sizeHint"> + <size> + <width>225</width> + <height>20</height> + </size> + </property> + </spacer> + <widget class="KComboBox" row="0" column="1"> + <item> + <property name="text"> + <string>48000</string> + </property> + </item> + <item> + <property name="text"> + <string>44100</string> + </property> + </item> + <item> + <property name="text"> + <string>22050</string> + </property> + </item> + <item> + <property name="text"> + <string>11025</string> + </property> + </item> + <property name="name"> + <cstring>editRate</cstring> + </property> + <property name="sizePolicy"> + <sizepolicy> + <hsizetype>3</hsizetype> + <vsizetype>0</vsizetype> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + </widget> + <widget class="TQLabel" row="2" column="0"> + <property name="name"> + <cstring>lableEndianess</cstring> + </property> + <property name="text"> + <string>Endianess</string> + </property> + </widget> + <spacer row="3" column="2"> + <property name="name"> + <cstring>spacer1_3</cstring> + </property> + <property name="orientation"> + <enum>Horizontal</enum> + </property> + <property name="sizeType"> + <enum>Minimum</enum> + </property> + <property name="sizeHint"> + <size> + <width>225</width> + <height>20</height> + </size> + </property> + </spacer> + <widget class="KComboBox" row="3" column="1"> + <item> + <property name="text"> + <string>Stereo</string> + </property> + </item> + <item> + <property name="text"> + <string>Mono</string> + </property> + </item> + <property name="name"> + <cstring>editChannels</cstring> + </property> + <property name="sizePolicy"> + <sizepolicy> + <hsizetype>3</hsizetype> + <vsizetype>0</vsizetype> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + </widget> + <widget class="TQLabel" row="0" column="0"> + <property name="name"> + <cstring>labelRate</cstring> + </property> + <property name="text"> + <string>Sample Rate</string> + </property> + </widget> + <widget class="KComboBox" row="2" column="1"> + <item> + <property name="text"> + <string>Little Endian</string> + </property> + </item> + <item> + <property name="text"> + <string>Big Endian</string> + </property> + </item> + <property name="name"> + <cstring>editEndianess</cstring> + </property> + <property name="sizePolicy"> + <sizepolicy> + <hsizetype>3</hsizetype> + <vsizetype>0</vsizetype> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + </widget> + <widget class="KComboBox" row="1" column="1"> + <item> + <property name="text"> + <string>16</string> + </property> + </item> + <item> + <property name="text"> + <string>8</string> + </property> + </item> + <property name="name"> + <cstring>editBits</cstring> + </property> + <property name="sizePolicy"> + <sizepolicy> + <hsizetype>3</hsizetype> + <vsizetype>0</vsizetype> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + </widget> + <widget class="TQLabel" row="3" column="0"> + <property name="name"> + <cstring>lableChannels</cstring> + </property> + <property name="text"> + <string>Channels</string> + </property> + </widget> + <widget class="TQLabel" row="1" column="0"> + <property name="name"> + <cstring>lableBits</cstring> + </property> + <property name="text"> + <string>Sample Bits</string> + </property> + </widget> + <widget class="KComboBox" row="1" column="2"> + <item> + <property name="text"> + <string>Signed</string> + </property> + </item> + <item> + <property name="text"> + <string>Unsigned</string> + </property> + </item> + <property name="name"> + <cstring>editSign</cstring> + </property> + <property name="sizePolicy"> + <sizepolicy> + <hsizetype>3</hsizetype> + <vsizetype>0</vsizetype> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + </widget> + <spacer row="2" column="2"> + <property name="name"> + <cstring>spacer1_2</cstring> + </property> + <property name="orientation"> + <enum>Horizontal</enum> + </property> + <property name="sizeType"> + <enum>Minimum</enum> + </property> + <property name="sizeHint"> + <size> + <width>225</width> + <height>20</height> + </size> + </property> + </spacer> + </grid> + </widget> + <spacer row="1" column="0"> + <property name="name"> + <cstring>spacer131</cstring> + </property> + <property name="orientation"> + <enum>Vertical</enum> + </property> + <property name="sizeType"> + <enum>Expanding</enum> + </property> + <property name="sizeHint"> + <size> + <width>20</width> + <height>5</height> + </size> + </property> + </spacer> + </grid> + </widget> + <widget class="TQWidget"> + <property name="name"> + <cstring>TabPage_3</cstring> + </property> + <attribute name="title"> + <string>&Buffers</string> + </attribute> + <grid> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <property name="margin"> + <number>11</number> + </property> + <widget class="TQLayoutWidget" row="0" column="0"> + <property name="name"> + <cstring>layout5</cstring> + </property> + <grid> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="TQSpinBox" row="0" column="1"> + <property name="name"> + <cstring>editBufferSize</cstring> + </property> + <property name="suffix"> + <string> kB</string> + </property> + <property name="maxValue"> + <number>2048</number> + </property> + <property name="minValue"> + <number>64</number> + </property> + <property name="lineStep"> + <number>16</number> + </property> + <property name="value"> + <number>256</number> + </property> + </widget> + <widget class="TQLabel" row="0" column="0"> + <property name="name"> + <cstring>labelEditBufferSize</cstring> + </property> + <property name="text"> + <string>Encoding Buffer Size</string> + </property> + </widget> + <widget class="TQSpinBox" row="1" column="1"> + <property name="name"> + <cstring>editBufferCount</cstring> + </property> + <property name="minValue"> + <number>3</number> + </property> + <property name="value"> + <number>3</number> + </property> + </widget> + <widget class="TQLabel" row="1" column="0"> + <property name="name"> + <cstring>labelEditBufferCount</cstring> + </property> + <property name="text"> + <string>Number of Buffers</string> + </property> + </widget> + </grid> + </widget> + <spacer row="1" column="0"> + <property name="name"> + <cstring>spacer132_2</cstring> + </property> + <property name="orientation"> + <enum>Vertical</enum> + </property> + <property name="sizeType"> + <enum>Expanding</enum> + </property> + <property name="sizeHint"> + <size> + <width>20</width> + <height>16</height> + </size> + </property> + </spacer> + </grid> + </widget> + <widget class="TQWidget"> + <property name="name"> + <cstring>TabPage_4</cstring> + </property> + <attribute name="title"> + <string>Pre-Recordin&g</string> + </attribute> + <grid> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <property name="margin"> + <number>11</number> + </property> + <widget class="TQLayoutWidget" row="0" column="0"> + <property name="name"> + <cstring>layout68</cstring> + </property> + <vbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="TQLayoutWidget"> + <property name="name"> + <cstring>layout6</cstring> + </property> + <hbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="TQCheckBox"> + <property name="name"> + <cstring>m_checkboxPreRecordingEnable</cstring> + </property> + <property name="text"> + <string>E&nable</string> + </property> + <property name="accel"> + <string>Alt+N</string> + </property> + </widget> + <spacer> + <property name="name"> + <cstring>spacer7</cstring> + </property> + <property name="orientation"> + <enum>Horizontal</enum> + </property> + <property name="sizeType"> + <enum>Expanding</enum> + </property> + <property name="sizeHint"> + <size> + <width>380</width> + <height>20</height> + </size> + </property> + </spacer> + </hbox> + </widget> + <widget class="TQLayoutWidget"> + <property name="name"> + <cstring>layout7</cstring> + </property> + <hbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="TQLabel"> + <property name="name"> + <cstring>m_labelPreRecordingTime</cstring> + </property> + <property name="enabled"> + <bool>false</bool> + </property> + <property name="text"> + <string>PreRecording Time</string> + </property> + </widget> + <widget class="TQSpinBox"> + <property name="name"> + <cstring>m_spinboxPreRecordingSeconds</cstring> + </property> + <property name="enabled"> + <bool>false</bool> + </property> + <property name="suffix"> + <string> s</string> + </property> + <property name="maxValue"> + <number>999</number> + </property> + <property name="minValue"> + <number>1</number> + </property> + <property name="lineStep"> + <number>1</number> + </property> + <property name="value"> + <number>10</number> + </property> + </widget> + </hbox> + </widget> + </vbox> + </widget> + <spacer row="1" column="0"> + <property name="name"> + <cstring>spacer132_3</cstring> + </property> + <property name="orientation"> + <enum>Vertical</enum> + </property> + <property name="sizeType"> + <enum>Expanding</enum> + </property> + <property name="sizeHint"> + <size> + <width>20</width> + <height>16</height> + </size> + </property> + </spacer> + </grid> + </widget> + </widget> + </grid> +</widget> +<customwidgets> +</customwidgets> +<connections> + <connection> + <sender>m_checkboxPreRecordingEnable</sender> + <signal>toggled(bool)</signal> + <receiver>m_spinboxPreRecordingSeconds</receiver> + <slot>setEnabled(bool)</slot> + </connection> + <connection> + <sender>m_checkboxPreRecordingEnable</sender> + <signal>toggled(bool)</signal> + <receiver>m_labelPreRecordingTime</receiver> + <slot>setEnabled(bool)</slot> + </connection> +</connections> +<layoutdefaults spacing="6" margin="0"/> +<includehints> + <includehint>kurlrequester.h</includehint> + <includehint>klineedit.h</includehint> + <includehint>kpushbutton.h</includehint> + <includehint>kcombobox.h</includehint> + <includehint>kcombobox.h</includehint> + <includehint>kcombobox.h</includehint> + <includehint>kcombobox.h</includehint> + <includehint>kcombobox.h</includehint> + <includehint>kcombobox.h</includehint> +</includehints> +</UI> diff --git a/tderadio3/plugins/recording/recording-configuration.cpp b/tderadio3/plugins/recording/recording-configuration.cpp new file mode 100644 index 0000000..f35f7dd --- /dev/null +++ b/tderadio3/plugins/recording/recording-configuration.cpp @@ -0,0 +1,414 @@ +/*************************************************************************** + recording-configuration.cpp - description + ------------------- + begin : So Aug 31 2003 + copyright : (C) 2003 by Martin Witte + email : witte@kawo1.rwth-aachen.de + ***************************************************************************/ + +/*************************************************************************** + * * + * 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 "recording-configuration.h" +//#include "recording-context.h" + +#include <kurlrequester.h> +#include <kcombobox.h> +#include <tqspinbox.h> +#include <tqlabel.h> +#include <tqcheckbox.h> + +#include <ktabwidget.h> + + +RecordingConfiguration::RecordingConfiguration (TQWidget *parent) + : RecordingConfigurationUI(parent), + m_dirty(true), + m_ignore_gui_updates(false) +{ + editDirectory->setMode(KFile::Directory | KFile::ExistingOnly); + + TQObject::connect(editFileFormat, TQT_SIGNAL(activated(int)), + this, TQT_SLOT(slotFormatSelectionChanged())); + TQObject::connect(editBits, TQT_SIGNAL(activated(int)), + this, TQT_SLOT(slotFormatSelectionChanged())); + + connect(editRate, TQT_SIGNAL(activated(int)), TQT_SLOT(slotSetDirty())); + connect(editBits, TQT_SIGNAL(activated(int)), TQT_SLOT(slotSetDirty())); + connect(editSign, TQT_SIGNAL(activated(int)), TQT_SLOT(slotSetDirty())); + connect(editEndianess, TQT_SIGNAL(activated(int)), TQT_SLOT(slotSetDirty())); + connect(editChannels, TQT_SIGNAL(activated(int)), TQT_SLOT(slotSetDirty())); + connect(editFileFormat, TQT_SIGNAL(activated(int)), TQT_SLOT(slotSetDirty())); + connect(editMP3Quality, TQT_SIGNAL(valueChanged(int)), TQT_SLOT(slotSetDirty())); + connect(editOggQuality, TQT_SIGNAL(valueChanged(int)), TQT_SLOT(slotSetDirty())); + connect(editDirectory, TQT_SIGNAL(textChanged(const TQString &)), TQT_SLOT(slotSetDirty())); + connect(editBufferSize, TQT_SIGNAL(valueChanged(int)), TQT_SLOT(slotSetDirty())); + connect(editBufferCount, TQT_SIGNAL(valueChanged(int)), TQT_SLOT(slotSetDirty())); + connect(m_spinboxPreRecordingSeconds, TQT_SIGNAL(valueChanged(int)), TQT_SLOT(slotSetDirty())); + connect(m_checkboxPreRecordingEnable, TQT_SIGNAL(toggled(bool)), TQT_SLOT(slotSetDirty())); + +// attention: remove items with higher index first ;-) otherwise indexes are not valid +#ifndef HAVE_OGG + editFileFormat->removeItem(FORMAT_OGG_IDX_ORG); + delete editOggQuality; + editOggQuality = NULL; + delete labelOggQuality; + labelOggQuality = NULL; +#endif +#ifndef HAVE_LAME + editFileFormat->removeItem(FORMAT_MP3_IDX_ORG); + delete editMP3Quality; + editMP3Quality = NULL; + delete labelMP3Quality; + labelMP3Quality = NULL; +#endif +} + + +RecordingConfiguration::~RecordingConfiguration () +{ +} + + +void RecordingConfiguration::setGUIBuffers(const RecordingConfig &c) +{ + editBufferSize->setValue(c.m_EncodeBufferSize / 1024); + editBufferCount->setValue(c.m_EncodeBufferCount); +} + +void RecordingConfiguration::setGUIDirectories(const RecordingConfig &c) +{ + editDirectory->setURL(c.m_Directory); +} + +void RecordingConfiguration::setGUISoundFormat(const RecordingConfig &c) +{ + switch (c.m_SoundFormat.m_SampleBits) { + case 8 : editBits->setCurrentItem(BITS_8_IDX ); break; + case 16: editBits->setCurrentItem(BITS_16_IDX); break; + default: editBits->setCurrentItem(BITS_16_IDX); + } + switch (c.m_SoundFormat.m_Channels) { + case 1 : editChannels->setCurrentItem(CHANNELS_MONO_IDX); break; + case 2 : editChannels->setCurrentItem(CHANNELS_STEREO_IDX); break; + default: editChannels->setCurrentItem(CHANNELS_STEREO_IDX); break; + } + switch (c.m_SoundFormat.m_IsSigned) { + case 0 : editSign->setCurrentItem(SIGN_UNSIGNED_IDX); break; + case 1 : editSign->setCurrentItem(SIGN_SIGNED_IDX); break; + default: editSign->setCurrentItem(SIGN_SIGNED_IDX); break; + } + switch (c.m_SoundFormat.m_SampleRate) { + case 48000: editRate->setCurrentItem(RATE_48000_IDX); break; + case 44100: editRate->setCurrentItem(RATE_44100_IDX); break; + case 22050: editRate->setCurrentItem(RATE_22050_IDX); break; + case 11025: editRate->setCurrentItem(RATE_11025_IDX); break; + default: editRate->setCurrentItem(RATE_44100_IDX); break; + } + switch (c.m_SoundFormat.m_Endianess) { + case BIG_ENDIAN : editEndianess->setCurrentItem(ENDIAN_BIG_IDX); break; + case LITTLE_ENDIAN : editEndianess->setCurrentItem(ENDIAN_LITTLE_IDX); break; + default: editEndianess->setCurrentItem(ENDIAN_LITTLE_IDX); break; + } +} + +void RecordingConfiguration::setGUIOutputFormat(const RecordingConfig &c) +{ + switch (c.m_OutputFormat) { + case RecordingConfig::outputWAV: editFileFormat->setCurrentItem(FORMAT_WAV_IDX); break; + case RecordingConfig::outputAIFF: editFileFormat->setCurrentItem(FORMAT_AIFF_IDX); break; + case RecordingConfig::outputAU: editFileFormat->setCurrentItem(FORMAT_AU_IDX); break; + case RecordingConfig::outputRAW: editFileFormat->setCurrentItem(FORMAT_RAW_IDX); break; +#ifdef HAVE_LAME + case RecordingConfig::outputMP3: editFileFormat->setCurrentItem(FORMAT_MP3_IDX); break; +#endif +#ifdef HAVE_OGG + case RecordingConfig::outputOGG: editFileFormat->setCurrentItem(FORMAT_OGG_IDX); break; +#endif + default: editFileFormat->setCurrentItem(FORMAT_WAV_IDX); break; + } +} + +void RecordingConfiguration::setGUIEncoderQuality(const RecordingConfig &c) +{ +#ifdef HAVE_LAME + editMP3Quality->setValue(c.m_mp3Quality); +#endif +#ifdef HAVE_OGG + editOggQuality->setValue((int)(c.m_oggQuality * 9)); +#endif +} + + +void RecordingConfiguration::setGUIPreRecording(const RecordingConfig &c) +{ + m_spinboxPreRecordingSeconds->setValue(c.m_PreRecordingSeconds); + m_checkboxPreRecordingEnable->setChecked(c.m_PreRecordingEnable); +} + + +void RecordingConfiguration::slotOK() +{ + if (m_dirty) { + storeConfig(); + sendRecordingConfig(m_RecordingConfig); + m_dirty = false; + } +} + + +void RecordingConfiguration::storeConfig() +{ + RecordingConfig &c = m_RecordingConfig; + + c.m_EncodeBufferSize = editBufferSize->value() * 1024; + c.m_EncodeBufferCount = editBufferCount->value(); + + c.m_Directory = editDirectory->url(); + + switch(editRate->currentItem()) { + case RATE_48000_IDX: c.m_SoundFormat.m_SampleRate = 48000; break; + case RATE_44100_IDX: c.m_SoundFormat.m_SampleRate = 44100; break; + case RATE_22050_IDX: c.m_SoundFormat.m_SampleRate = 22050; break; + case RATE_11025_IDX: c.m_SoundFormat.m_SampleRate = 11025; break; + default: c.m_SoundFormat.m_SampleRate = 44100; break; + } + switch(editChannels->currentItem()) { + case CHANNELS_MONO_IDX: c.m_SoundFormat.m_Channels = 1; break; + case CHANNELS_STEREO_IDX: c.m_SoundFormat.m_Channels = 2; break; + default: c.m_SoundFormat.m_Channels = 2; break; + } + switch(editSign->currentItem()) { + case SIGN_UNSIGNED_IDX: c.m_SoundFormat.m_IsSigned = false; break; + case SIGN_SIGNED_IDX: c.m_SoundFormat.m_IsSigned = true; break; + default: c.m_SoundFormat.m_IsSigned = true; break; + } + switch(editEndianess->currentItem()) { + case ENDIAN_LITTLE_IDX: c.m_SoundFormat.m_Endianess = LITTLE_ENDIAN; break; + case ENDIAN_BIG_IDX: c.m_SoundFormat.m_Endianess = BIG_ENDIAN; break; + default: c.m_SoundFormat.m_Endianess = LITTLE_ENDIAN; break; + } + switch(editBits->currentItem()) { + case BITS_8_IDX: c.m_SoundFormat.m_SampleBits = 8; break; + case BITS_16_IDX: c.m_SoundFormat.m_SampleBits = 16; break; + default: c.m_SoundFormat.m_SampleBits = 16; break; + } + switch(editFileFormat->currentItem()) { + case FORMAT_WAV_IDX: c.m_OutputFormat = RecordingConfig::outputWAV; break; + case FORMAT_AIFF_IDX: c.m_OutputFormat = RecordingConfig::outputAIFF; break; + case FORMAT_AU_IDX: c.m_OutputFormat = RecordingConfig::outputAU; break; + case FORMAT_RAW_IDX: c.m_OutputFormat = RecordingConfig::outputRAW; break; +#ifdef HAVE_LAME + case FORMAT_MP3_IDX: c.m_OutputFormat = RecordingConfig::outputMP3; break; +#endif +#ifdef HAVE_OGG + case FORMAT_OGG_IDX: c.m_OutputFormat = RecordingConfig::outputOGG; break; +#endif + default: c.m_OutputFormat = RecordingConfig::outputWAV; break; + } +#ifdef HAVE_LAME + c.m_mp3Quality = editMP3Quality->value(); +#endif +#ifdef HAVE_OGG + c.m_oggQuality = ((float)editOggQuality->value()) / 9.0f; +#endif + + c.m_PreRecordingEnable = m_checkboxPreRecordingEnable->isChecked(); + c.m_PreRecordingSeconds = m_spinboxPreRecordingSeconds->value(); + + c.checkFormatSettings(); +} + + +void RecordingConfiguration::slotCancel() +{ + if (m_dirty) { + noticeRecordingConfigChanged(m_RecordingConfig); + m_dirty = false; + } +} + + +void RecordingConfiguration::slotFormatSelectionChanged() +{ + int bitsIDX = editBits->currentItem(); + int formatIDX = editFileFormat->currentItem(); + + int endianTest = 0x04030201; + bool littleEndian = ((char*)&endianTest)[0] == 0x01; + +#ifdef HAVE_LAME + editMP3Quality ->setEnabled(false); + labelMP3Quality->setEnabled(false); +#endif +#ifdef HAVE_OGG + editOggQuality ->setEnabled(false); + labelOggQuality->setEnabled(false); +#endif + + editBits->setEnabled(true); + + if (formatIDX == FORMAT_MP3_IDX) { + editBits->setDisabled(true); + editBits->setCurrentItem(BITS_16_IDX); + editSign->setDisabled(true); + editSign->setCurrentItem(SIGN_SIGNED_IDX); +#ifdef HAVE_LAME + editMP3Quality ->setEnabled(true); + labelMP3Quality->setEnabled(true); +#endif + } else if (formatIDX == FORMAT_OGG_IDX) { + editBits->setDisabled(true); + editBits->setCurrentItem(BITS_16_IDX); + editSign->setDisabled(true); + editSign->setCurrentItem(SIGN_SIGNED_IDX); +#ifdef HAVE_OGG + editOggQuality ->setEnabled(true); + labelOggQuality->setEnabled(true); +#endif + } else { + if (bitsIDX == BITS_8_IDX) { + if (formatIDX == FORMAT_RAW_IDX || formatIDX == FORMAT_AIFF_IDX) { + editSign->setDisabled(false); + } else { + editSign->setDisabled(true); + editSign->setCurrentItem(formatIDX == FORMAT_WAV_IDX ? SIGN_UNSIGNED_IDX : SIGN_SIGNED_IDX); + } + } else { + editSign->setDisabled(true); + editSign->setCurrentItem(SIGN_SIGNED_IDX); + } + } + + switch (formatIDX) { + case FORMAT_RAW_IDX : + editEndianess->setDisabled(false); + break; +#ifdef HAVE_LAME + case FORMAT_MP3_IDX : + editEndianess->setCurrentItem(littleEndian ? ENDIAN_LITTLE_IDX : ENDIAN_BIG_IDX); + editEndianess->setDisabled(true); + break; +#endif +#ifdef HAVE_OGG + case FORMAT_OGG_IDX : + editEndianess->setCurrentItem(littleEndian ? ENDIAN_LITTLE_IDX : ENDIAN_BIG_IDX); + editEndianess->setDisabled(true); + break; +#endif + default: + editEndianess->setDisabled(true); + if (formatIDX == FORMAT_AIFF_IDX || formatIDX == FORMAT_AU_IDX) { + editEndianess->setCurrentItem(ENDIAN_BIG_IDX); + } else { + editEndianess->setCurrentItem(ENDIAN_LITTLE_IDX); + } + break; + } +} + + + +bool RecordingConfiguration::noticeEncoderBufferChanged (size_t BufferSize, size_t BufferCount) +{ + m_ignore_gui_updates = true; + m_RecordingConfig.m_EncodeBufferSize = BufferSize; + m_RecordingConfig.m_EncodeBufferCount = BufferCount; + setGUIBuffers(m_RecordingConfig); + slotFormatSelectionChanged(); + m_ignore_gui_updates = false; + return true; +} + + +bool RecordingConfiguration::noticeSoundFormatChanged (const SoundFormat &sf) +{ + m_ignore_gui_updates = true; + m_RecordingConfig.m_SoundFormat = sf; + setGUISoundFormat(m_RecordingConfig); + slotFormatSelectionChanged(); + m_ignore_gui_updates = false; + return true; +} + + +bool RecordingConfiguration::noticeMP3QualityChanged (int q) +{ + m_ignore_gui_updates = true; + m_RecordingConfig.m_mp3Quality = q; + setGUIEncoderQuality(m_RecordingConfig); + slotFormatSelectionChanged(); + m_ignore_gui_updates = false; + return true; +} + +bool RecordingConfiguration::noticeOggQualityChanged (float q) +{ + m_ignore_gui_updates = true; + m_RecordingConfig.m_oggQuality = q; + setGUIEncoderQuality(m_RecordingConfig); + slotFormatSelectionChanged(); + m_ignore_gui_updates = false; + return true; +} + +bool RecordingConfiguration::noticeRecordingDirectoryChanged(const TQString &dir) +{ + m_ignore_gui_updates = true; + m_RecordingConfig.m_Directory = dir; + setGUIDirectories(m_RecordingConfig); + slotFormatSelectionChanged(); + m_ignore_gui_updates = false; + return true; +} + +bool RecordingConfiguration::noticeOutputFormatChanged (RecordingConfig::OutputFormat of) +{ + m_ignore_gui_updates = true; + m_RecordingConfig.m_OutputFormat = of; + setGUIOutputFormat(m_RecordingConfig); + slotFormatSelectionChanged(); + m_ignore_gui_updates = false; + return true; +} + +bool RecordingConfiguration::noticePreRecordingChanged (bool enable, int seconds) +{ + m_ignore_gui_updates = true; + m_RecordingConfig.m_PreRecordingEnable = enable; + m_RecordingConfig.m_PreRecordingSeconds = seconds; + setGUIPreRecording(m_RecordingConfig); + m_ignore_gui_updates = false; + return true; +} + +bool RecordingConfiguration::noticeRecordingConfigChanged(const RecordingConfig &c) +{ + m_ignore_gui_updates = true; + m_RecordingConfig = c; + setGUIBuffers(c); + setGUIDirectories(c); + setGUISoundFormat(c); + setGUIOutputFormat(c); + setGUIEncoderQuality(c); + setGUIPreRecording(c); + slotFormatSelectionChanged(); + m_ignore_gui_updates = false; + return true; +} + +void RecordingConfiguration::slotSetDirty() +{ + if (!m_ignore_gui_updates) { + m_dirty = true; + } +} + + +#include "recording-configuration.moc" diff --git a/tderadio3/plugins/recording/recording-configuration.h b/tderadio3/plugins/recording/recording-configuration.h new file mode 100644 index 0000000..90c2144 --- /dev/null +++ b/tderadio3/plugins/recording/recording-configuration.h @@ -0,0 +1,127 @@ +/*************************************************************************** + recording-configuration.h - description + ------------------- + begin : So Aug 31 2003 + copyright : (C) 2003 by Martin Witte + email : witte@kawo1.rwth-aachen.de + ***************************************************************************/ + +/*************************************************************************** + * * + * 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. * + * * + ***************************************************************************/ + +#ifndef KRADIO_RECORDING_CONFIGURATION_H +#define KRADIO_RECORDING_CONFIGURATION_H + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include "../../src/include/soundformat.h" + +#include "recording-config.h" +#include "reccfg_interfaces.h" +#include "recording-configuration-ui.h" + +#define RATE_48000_IDX 0 +#define RATE_44100_IDX 1 +#define RATE_22050_IDX 2 +#define RATE_11025_IDX 3 + +#define CHANNELS_STEREO_IDX 0 +#define CHANNELS_MONO_IDX 1 + +#define SIGN_SIGNED_IDX 0 +#define SIGN_UNSIGNED_IDX 1 + +#define BITS_16_IDX 0 +#define BITS_8_IDX 1 + +#define ENDIAN_LITTLE_IDX 0 +#define ENDIAN_BIG_IDX 1 + +#define FORMAT_RAW_IDX 0 +#define FORMAT_WAV_IDX 1 +#define FORMAT_AIFF_IDX 2 +#define FORMAT_AU_IDX 3 +#define NEXT_IDX1 4 + +#define FORMAT_MP3_IDX_ORG 4 +#define FORMAT_OGG_IDX_ORG 5 + + +#ifdef HAVE_LAME + #define FORMAT_MP3_IDX NEXT_IDX1 + #define NEXT_IDX2 (NEXT_IDX1+1) +#else + #define FORMAT_MP3_IDX (-1) + #define NEXT_IDX2 NEXT_IDX1 +#endif + +#ifdef HAVE_OGG + #define FORMAT_OGG_IDX NEXT_IDX2 + #define NEXT_IDX3 (NEXT_IDX2+1) +#else + #define FORMAT_OGG_IDX (-1) + #define NEXT_IDX3 NEXT_IDX2 +#endif + + + + + + +class RecordingConfiguration : public RecordingConfigurationUI, + public IRecCfgClient +{ +Q_OBJECT + +public : + RecordingConfiguration (TQWidget *parent); + ~RecordingConfiguration (); + +// IRecCfgClient + + bool noticeEncoderBufferChanged (size_t BufferSize, size_t BufferCount); + bool noticeSoundFormatChanged (const SoundFormat &sf); + bool noticeMP3QualityChanged (int q); + bool noticeOggQualityChanged (float q); + bool noticeRecordingDirectoryChanged(const TQString &dir); + bool noticeOutputFormatChanged (RecordingConfig::OutputFormat of); + bool noticePreRecordingChanged (bool enable, int seconds); + bool noticeRecordingConfigChanged (const RecordingConfig &cfg); + +protected slots: + + void slotOK(); + void slotCancel(); + void slotSetDirty(); + + void slotFormatSelectionChanged(); + +protected: + + void storeConfig(); + + void setGUIBuffers(const RecordingConfig &c); + void setGUIDirectories(const RecordingConfig &c); + void setGUISoundFormat(const RecordingConfig &c); + void setGUIOutputFormat(const RecordingConfig &c); + void setGUIPreRecording(const RecordingConfig &c); + void setGUIEncoderQuality(const RecordingConfig &c); + + RecordingConfig m_RecordingConfig; + + bool m_dirty; + bool m_ignore_gui_updates; +}; + + + + +#endif diff --git a/tderadio3/plugins/recording/recording-datamonitor.cpp b/tderadio3/plugins/recording/recording-datamonitor.cpp new file mode 100644 index 0000000..f2bbe8e --- /dev/null +++ b/tderadio3/plugins/recording/recording-datamonitor.cpp @@ -0,0 +1,278 @@ +/*************************************************************************** + recording-monitor-widget.cpp - description + ------------------- + begin : So Sep 7 2003 + copyright : (C) 2003 by Martin Witte + email : witte@kawo1.rwth-aachen.de + ***************************************************************************/ + +/*************************************************************************** + * * + * 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 "recording-datamonitor.h" +//#include "recording-context.h" +#include <math.h> + +#include <tqpainter.h> +#include <tqimage.h> +#include <tqpixmap.h> +#include <kimageeffect.h> // fading, blending, ... +#include <kpixmapio.h> // fast conversion between TQPixmap/TQImage +#include <limits.h> +#include <stdlib.h> + +#include <klocale.h> + +#define CHANNEL_H_MIN 20 +#define BLOCK_W_MIN 10 +#define W_MIN (20 * (BLOCK_W_MIN)) + +RecordingDataMonitor::RecordingDataMonitor(TQWidget *parent, const char *name) + : TQFrame(parent, name), + m_channelsMax(NULL), + m_channelsAvg(NULL), + m_maxValue(INT_MAX), + m_channels(0), + m_pActiveBlocks(NULL) +{ + setFrameStyle(Box | Sunken); + setLineWidth(1); + setMidLineWidth(1); + + setChannels(2); + + setColors(TQColor(20, 244, 20), + TQColor(10, 117, 10)); + + setSizePolicy(TQSizePolicy(TQSizePolicy::Expanding, TQSizePolicy::Expanding)); +} + + +RecordingDataMonitor::~RecordingDataMonitor() +{ + if (m_channelsMax) delete[] m_channelsMax; + if (m_channelsAvg) delete[] m_channelsAvg; + if (m_pActiveBlocks) delete[] m_pActiveBlocks; +} + + + +// own stuff + +void RecordingDataMonitor::setChannels(int n) +{ + if (n != m_channels) { + if (m_channelsMax) delete[] m_channelsMax; + if (m_channelsAvg) delete[] m_channelsAvg; + if (m_pActiveBlocks) delete[] m_pActiveBlocks; + m_channels = n > 0 ? n : 0; + if (m_channels > 0) { + m_channelsMax = new int[m_channels]; + m_channelsAvg = new double[m_channels]; + m_pActiveBlocks = new int[m_channels]; + for (int i = 0; i < m_channels; ++i) { + m_pActiveBlocks[i] = 0; + } + } else { + m_channelsMax = NULL; + m_channelsAvg = NULL; + m_pActiveBlocks = NULL; + } + } + + for (int i = 0; i < m_channels; ++i) { + m_channelsMax[i] = 0; + m_channelsAvg[i] = 0; + } + setMinimumSize(TQSize(W_MIN, (m_channels + 1 )* CHANNEL_H_MIN)); +} + + +// QT/KDE ... + +void RecordingDataMonitor::drawContents(TQPainter *painter) +{ + if (painter) + internalDrawContents(*painter, true); +} + +void RecordingDataMonitor::internalDrawContents(TQPainter &painter, bool repaintAll) +{ + if (m_channels <= 0) return; + TQRect r = contentsRect(); + + TQPen activePen (colorGroup().color(TQColorGroup::Text), 1); + TQPen inactivePen (colorGroup().color(TQColorGroup::Mid), 1); + TQBrush activeBrush = colorGroup().brush(TQColorGroup::Text); + TQBrush inactiveBrush = colorGroup().brush(TQColorGroup::Mid); + TQBrush yellowBrush(TQColor(255,255,0)); + TQBrush orangeBrush(TQColor(255,192,0)); + TQBrush redBrush (TQColor(255,0, 0)); + + + double ranges [5] = { 0.75, 0.83, 0.91, 1.0, 999 }; + TQBrush *brushes[5] = { &activeBrush, &yellowBrush, &orangeBrush, &redBrush, &redBrush }; + + painter.setBrush( isEnabled() ? activeBrush : inactiveBrush); + + int nBlocks = (r.width()-1) / BLOCK_W_MIN; + int xoffs = (r.width()-1) % BLOCK_W_MIN; + int chHeight = (r.height()-1-CHANNEL_H_MIN) / m_channels; + int yoffs = (r.height()-1) % m_channels; + + double min_dB = 20*log10(1 / (double)m_maxValue ); + + int x0 = xoffs/2 + r.top(); + int y = yoffs/2 + r.left(); + for (int c = 0; c < m_channels; ++c) { + int x = x0; + + + int startBlock = 0; + int endBlock = nBlocks - 1; + int oldActiveBlocks = m_pActiveBlocks[c]; + + double dBMax = isEnabled() ? 20*log10(m_channelsMax[c] / (double)m_maxValue ) : min_dB; + + m_pActiveBlocks[c] = m_channelsMax[c] ? (int)rint(nBlocks * (min_dB - dBMax) / min_dB) : 0; + + if (!repaintAll) { + if (oldActiveBlocks > m_pActiveBlocks[c]) { + startBlock = m_pActiveBlocks[c]; + endBlock = oldActiveBlocks - 1; + } else { + startBlock = oldActiveBlocks; + endBlock = m_pActiveBlocks[c]-1; + } + } + + int range = 0; + + x += BLOCK_W_MIN * startBlock; + for (int b = startBlock; b <= endBlock; ++b) { + while (b >= nBlocks * ranges[range]) ++range; + painter.fillRect(x+1, y+1, BLOCK_W_MIN-1, chHeight-1, + b < m_pActiveBlocks[c] ? *brushes[range] : inactiveBrush); + x += BLOCK_W_MIN; + } + + y += chHeight; + } + + if (repaintAll) { + TQFont f("Helvetica"); + painter.setPen (activePen); + f.setPixelSize(CHANNEL_H_MIN); + painter.setFont(f); + + int maxW = TQFontMetrics(f).width(i18n("%1 dB").arg((int)min_dB)); + int delta_dB = 5; + while (abs((long)min_dB) / delta_dB * maxW * 2 > r.width()) delta_dB *= 2; + + for (int dB = 0; dB >= min_dB; dB -= delta_dB) { + TQString txt = i18n("%1 dB").arg(dB); + int w = TQFontMetrics(f).width(txt); + int x = x0 + (int)(nBlocks * BLOCK_W_MIN * (min_dB - dB) / min_dB) - w; + if (x < x0) continue; + painter.drawText(x, y + CHANNEL_H_MIN, txt); + } + } +} + + +bool RecordingDataMonitor::setColors(const TQColor &activeText, + const TQColor &button) +{ + m_colorActiveText = activeText; + m_colorButton = button; + + TQPalette pl = palette(); + TQColorGroup cg = pl.inactive(); + + TQBrush fg = cg.brush(TQColorGroup::Foreground), + btn = cg.brush(TQColorGroup::Button), + lgt = cg.brush(TQColorGroup::Light), + drk = cg.brush(TQColorGroup::Dark), + mid = cg.brush(TQColorGroup::Mid), + txt = cg.brush(TQColorGroup::Text), + btx = cg.brush(TQColorGroup::BrightText), + bas = cg.brush(TQColorGroup::Base), + bg = cg.brush(TQColorGroup::Background); + + fg.setColor (m_colorActiveText); + btn.setColor(m_colorButton); + lgt.setColor(m_colorButton.light(180)); + drk.setColor(m_colorButton.light( 50)); + mid.setColor(m_colorButton.light( 75)); + txt.setColor(m_colorActiveText); + btx.setColor(m_colorActiveText); + bas.setColor(m_colorButton); + bg.setColor (m_colorButton); + + TQColorGroup ncg(fg, btn, lgt, drk, mid, txt, btx, bas, bg); + pl.setInactive(ncg); + pl.setActive(ncg); + setPalette(pl); + + if (parentWidget() && parentWidget()->backgroundPixmap() ){ + KPixmapIO io; + TQImage i = io.convertToImage(*parentWidget()->backgroundPixmap()); + KImageEffect::fade(i, 0.5, colorGroup().color(TQColorGroup::Dark)); + setPaletteBackgroundPixmap(io.convertToPixmap(i)); + setBackgroundOrigin(WindowOrigin); + } else { + setBackgroundColor(colorGroup().color(TQColorGroup::Button)); + } + + return true; +} + + +bool RecordingDataMonitor::noticeSoundStreamData(SoundStreamID /*id*/, + const SoundFormat &sf, const char *data, size_t size, size_t &/*consumed_size*/, + const SoundMetaData &/*md*/ +) +{ + if (!isEnabled()) + return false; + int nSamples = size / sf.frameSize(); + int sample_size = sf.sampleSize(); + + int bias = 0; + setChannels(sf.m_Channels); + int old_max = m_maxValue; + m_maxValue = sf.maxValue(); + if (!sf.m_IsSigned) { + m_maxValue /= 2; + bias = -m_maxValue; + } + + int c = 0; + for (int s = 0; s < nSamples; ++s, ++c, data += sample_size) { + if (c >= m_channels) c -= m_channels; // avoid slow c = s % m_channels + + int &m = m_channelsMax[c]; + int x = abs(sf.convertSampleToInt(data, false) + bias); + if (m < x) m = x; + m_channelsAvg[c] += x; + } + for (int i = 0; i < m_channels; ++i) + m_channelsAvg[i] /= nSamples; + + TQPainter paint(this); + if (m_maxValue != old_max) { + repaint(true); + } else { + internalDrawContents(paint, false); + } + return true; +} + + +#include "recording-datamonitor.moc" diff --git a/tderadio3/plugins/recording/recording-datamonitor.h b/tderadio3/plugins/recording/recording-datamonitor.h new file mode 100644 index 0000000..bcb7935 --- /dev/null +++ b/tderadio3/plugins/recording/recording-datamonitor.h @@ -0,0 +1,67 @@ +/*************************************************************************** + recording-monitor-widget.h - description + ------------------- + begin : So Sep 7 2003 + copyright : (C) 2003 by Martin Witte + email : witte@kawo1.rwth-aachen.de + ***************************************************************************/ + +/*************************************************************************** + * * + * 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. * + * * + ***************************************************************************/ + +#ifndef KRADIO_RECORDING_DATA_MONITOR +#define KRADIO_RECORDING_DATA_MONITOR + +#include <tqframe.h> +#include <tqcolor.h> + +//#include <kradio/interfaces/recording-interfaces.h> +#include "../../src/include/soundstreamclient_interfaces.h" + +class RecordingDataMonitor : public TQFrame//, + //public ISoundStreamClient + //public IRecordingClient +{ +Q_OBJECT + +public: + RecordingDataMonitor(TQWidget *parent, const char *name); + ~RecordingDataMonitor(); + + bool noticeSoundStreamData(SoundStreamID id, + const SoundFormat &sf, const char *data, size_t size, size_t &consumed_size, + const SoundMetaData &md); + +// QT/KDE ... + +protected: + + void drawContents(TQPainter *p); + void internalDrawContents(TQPainter &painter, bool repaintAll); +// own stuff ... + +protected: + + void setChannels(int n); + bool setColors(const TQColor &activeColor, const TQColor &bkgnd); + +// data +protected: + + int *m_channelsMax; // maximum absolute value recorded on each channel + double *m_channelsAvg; // average value recorded on each channel + int m_maxValue; // maximum absolute value possible for samples + int m_channels; + + TQColor m_colorActiveText, m_colorButton; + + int *m_pActiveBlocks; +}; + +#endif diff --git a/tderadio3/plugins/recording/recording-monitor.cpp b/tderadio3/plugins/recording/recording-monitor.cpp new file mode 100644 index 0000000..37edfe9 --- /dev/null +++ b/tderadio3/plugins/recording/recording-monitor.cpp @@ -0,0 +1,402 @@ +/*************************************************************************** + recording-monitor.cpp - description + ------------------- + begin : Mo Sep 1 2003 + copyright : (C) 2003 by Martin Witte + email : witte@kawo1.rwth-aachen.de + ***************************************************************************/ + +/*************************************************************************** + * * + * 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 "recording-monitor.h" +#include "recording-datamonitor.h" +#include "../../src/include/aboutwidget.h" + +#include <tqlabel.h> +#include <tqpushbutton.h> +#include <tqlayout.h> +#include <tqcheckbox.h> +#include <kcombobox.h> + +#include <klocale.h> +#include <tdeconfig.h> +#include <kaboutdata.h> + +RecordingMonitor::RecordingMonitor(const TQString &name) + : TQWidget(NULL, name.ascii()), + WidgetPluginBase(name, i18n("Recording Monitor")), + m_recording(false), + m_defaultStreamDescription(TQString()) +{ + setCaption(i18n("TDERadio Recording Monitor")); + + TQVBoxLayout *l = new TQVBoxLayout(this, 10, 4); + TQGridLayout *l0 = new TQGridLayout(l, 6, 2); + + l0->addWidget( new TQLabel(i18n("SoundStream"), this), 0, 0); + l0->addWidget(m_comboSoundStreamSelector = new KComboBox( this), 0, 1); + l0->addWidget( new TQLabel(i18n("Status"), this), 1, 0); + l0->addWidget(m_labelStatus = new TQLabel(i18n("<undefined>"), this), 1, 1); + l0->addWidget( new TQLabel(i18n("Recording File"), this), 2, 0); + l0->addWidget(m_labelFileName = new TQLabel(i18n("<undefined>"), this), 2, 1); + l0->addWidget( new TQLabel(i18n("File Size"), this), 3, 0); + l0->addWidget(m_labelSize = new TQLabel(i18n("<undefined>"), this), 3, 1); + l0->addWidget( new TQLabel(i18n("Recording Time"), this), 4, 0); + l0->addWidget(m_labelTime = new TQLabel(i18n("<undefined>"), this), 4, 1); + l0->addWidget( new TQLabel(i18n("Sample Rate"), this), 5, 0); + l0->addWidget(m_labelRate = new TQLabel(i18n("<undefined>"), this), 5, 1); + + TQPushButton *close = new TQPushButton(i18n("&Close"), this); + m_btnStartStop = new TQPushButton(i18n("&Record"), this); + TQObject::connect(close, TQT_SIGNAL(clicked()), this, TQT_SLOT(hide())); + TQObject::connect(m_btnStartStop, TQT_SIGNAL(clicked()), this, TQT_SLOT(slotStartStopRecording())); + + m_dataMonitor = new RecordingDataMonitor(this, NULL); + m_dataMonitor->setEnabled(false); + + TQHBoxLayout *hl0 = new TQHBoxLayout(l); + hl0->addWidget(m_dataMonitor); + + TQHBoxLayout *hl2 = new TQHBoxLayout(l); + hl2->addItem(new TQSpacerItem(10, 1)); + hl2->addWidget(close); + hl2->addWidget(m_btnStartStop); + hl2->addItem(new TQSpacerItem(10, 1)); + + + m_comboSoundStreamSelector->insertItem(i18n("nothing")); + TQObject::connect(m_comboSoundStreamSelector, TQT_SIGNAL(activated(int)), this, TQT_SLOT(slotStreamSelected(int))); + + updateRecordingButton(); +} + + +RecordingMonitor::~RecordingMonitor() +{ +} + +// WidgetPluginBase + +void RecordingMonitor::saveState (TDEConfig *config) const +{ + config->setGroup(TQString("recordingmonitor-") + name()); + + WidgetPluginBase::saveState(config); +} + + +void RecordingMonitor::restoreState (TDEConfig *config) +{ + config->setGroup(TQString("recordingmonitor-") + name()); + + WidgetPluginBase::restoreState(config, false); +} + + +bool RecordingMonitor::connectI(Interface *i) +{ + bool a = ISoundStreamClient::connectI(i); + bool b = WidgetPluginBase::connectI(i); + return a || b; +} + +bool RecordingMonitor::disconnectI(Interface *i) +{ + bool a = ISoundStreamClient::disconnectI(i); + bool b = WidgetPluginBase::disconnectI(i); + if (a) { + m_comboSoundStreamSelector->clear(); + m_SoundStreamID2idx.clear(); + m_idx2SoundStreamID.clear(); + m_comboSoundStreamSelector->insertItem(i18n("nothing")); + } + return a || b; +} + + +void RecordingMonitor::noticeConnectedI (ISoundStreamServer *s, bool pointer_valid) +{ + ISoundStreamClient::noticeConnectedI(s, pointer_valid); + if (s && pointer_valid) { + s->register4_notifySoundStreamCreated(this); + s->register4_notifySoundStreamClosed(this); + s->register4_notifySoundStreamChanged(this); + s->register4_notifySoundStreamData(this); + s->register4_sendStartRecordingWithFormat(this); + s->register4_sendStopRecording(this); + + TQMap<TQString, SoundStreamID> tmp; + queryEnumerateSoundStreams(tmp); + + m_comboSoundStreamSelector->clear(); + m_SoundStreamID2idx.clear(); + m_idx2SoundStreamID.clear(); + m_comboSoundStreamSelector->insertItem(i18n("nothing")); + TQMapConstIterator<TQString, SoundStreamID> end = tmp.end(); + for (TQMapConstIterator<TQString, SoundStreamID> it = tmp.begin(); it != end; ++it) { + int idx = m_comboSoundStreamSelector->count(); + m_comboSoundStreamSelector->insertItem(it.key()); + m_idx2SoundStreamID[idx] = *it; + m_SoundStreamID2idx[*it] = idx; + } + } +} + +ConfigPageInfo RecordingMonitor::createConfigurationPage() +{ + return ConfigPageInfo(); +} + +AboutPageInfo RecordingMonitor::createAboutPage() +{ +/* TDEAboutData aboutData("kradio", + NULL, + NULL, + I18N_NOOP("Recording Monitor Plugin for TDERadio"), + TDEAboutData::License_GPL, + "(c) 2002-2005 Martin Witte", + 0, + "http://sourceforge.net/projects/kradio", + 0); + aboutData.addAuthor("Martin Witte", "", "witte@kawo1.rwth-aachen.de"); + + return AboutPageInfo( + new TDERadioAboutWidget(aboutData, TDERadioAboutWidget::AbtTabbed), + i18n("Recording Monitor"), + i18n("Recording Monitor Plugin"), + "goto" + ); +*/ + return AboutPageInfo(); +} + + +void RecordingMonitor::show() +{ + WidgetPluginBase::pShow(); + TQWidget::show(); +} + + +void RecordingMonitor::showOnOrgDesktop() +{ + WidgetPluginBase::pShowOnOrgDesktop(); + //TQWidget::show(); +} + +void RecordingMonitor::hide() +{ + WidgetPluginBase::pHide(); + TQWidget::hide(); +} + + +void RecordingMonitor::showEvent(TQShowEvent *e) +{ + TQWidget::showEvent(e); + WidgetPluginBase::pShowEvent(e); + //m_comboSoundStreamSelector->setCurrentItem(1); + //slotStreamSelected(1); +} + + +void RecordingMonitor::hideEvent(TQHideEvent *e) +{ + TQWidget::hideEvent(e); + WidgetPluginBase::pHideEvent(e); + m_comboSoundStreamSelector->setCurrentItem(0); + slotStreamSelected(0); +} + + +void RecordingMonitor::slotStartStopRecording() +{ + if (m_currentStream.isValid()) { + if (m_recording) { + sendStopRecording(m_currentStream); + } else { + sendStartRecording(m_currentStream); + } + } + updateRecordingButton(); +} + + +bool RecordingMonitor::noticeSoundStreamCreated(SoundStreamID id) +{ + TQString tmp = TQString(); + querySoundStreamDescription(id, tmp); + + int idx = m_comboSoundStreamSelector->count(); + m_comboSoundStreamSelector->insertItem(tmp); + m_idx2SoundStreamID[idx] = id; + m_SoundStreamID2idx[id] = idx; + + if (tmp == m_defaultStreamDescription) { + m_comboSoundStreamSelector->setCurrentItem(idx); + slotStreamSelected(idx); + } + return true; +} + + +bool RecordingMonitor::noticeSoundStreamClosed(SoundStreamID id) +{ + if (m_SoundStreamID2idx.contains(id)) { + int idx = m_SoundStreamID2idx[id]; + m_idx2SoundStreamID.clear(); + m_SoundStreamID2idx.remove(id); + TQMapIterator<SoundStreamID, int> end = m_SoundStreamID2idx.end(); + for (TQMapIterator<SoundStreamID, int> it = m_SoundStreamID2idx.begin(); it != end; ++it) { + if (*it > idx) { + (*it)--; + } + m_idx2SoundStreamID[*it] = it.key(); + } + m_comboSoundStreamSelector->removeItem(idx); + slotStreamSelected(m_comboSoundStreamSelector->currentItem()); + return true; + } + return false; +} + + +bool RecordingMonitor::noticeSoundStreamChanged(SoundStreamID id) +{ + if (m_SoundStreamID2idx.contains(id)) { + int idx = m_SoundStreamID2idx[id]; + TQString tmp = TQString(); + querySoundStreamDescription(id, tmp); + m_comboSoundStreamSelector->changeItem(tmp, idx); + if (idx == m_comboSoundStreamSelector->currentItem()) { + m_defaultStreamDescription = tmp; + } + return true; + } + return false; +} + +bool RecordingMonitor::startRecordingWithFormat(SoundStreamID id, const SoundFormat &/*sf*/, SoundFormat &/*real_format*/) +{ + if (id == m_currentStream) { + m_recording = true; + updateRecordingButton(); + } + return false; +} + +bool RecordingMonitor::stopRecording(SoundStreamID id) +{ + if (id == m_currentStream) { + m_recording = false; + updateRecordingButton(); + } + return false; +} + +bool RecordingMonitor::noticeSoundStreamData(SoundStreamID id, + const SoundFormat &sf, const char *data, size_t size, size_t &consumed_size, + const SoundMetaData &md +) +{ + if (m_idx2SoundStreamID[m_comboSoundStreamSelector->currentItem()] == id) { + + m_labelFileName->setText(md.url().url()); + + double B = (double)md.position() + (double)size; + + double s = md.relativeTimestamp(); + + int m = (int)(s / 60); s -= 60 * m; + int h = m / 60; m %= 60; + int d = h / 24; h %= 24; + TQString time; + if (d) { + time.sprintf("%dd - %02d:%02d:%05.2f", d, h, m, s); + } else { + time.sprintf("%02d:%02d:%05.2f", h, m, s); + } + m_labelTime->setText(time); + + if (sf.m_Encoding == "raw") { + m_dataMonitor->setEnabled(true); + m_dataMonitor->noticeSoundStreamData(id, sf, data, size, consumed_size, md); + } else { + m_dataMonitor->setEnabled(false); + } + + double kB = B / 1024; + double MB = kB / 1024; + double GB = MB / 1024; + TQString str_size; + str_size = i18n("%1 Byte").arg(TDEGlobal::locale()->formatNumber((int)B, 0)); + if (kB > 1) str_size = i18n("%1 kB").arg(TDEGlobal::locale()->formatNumber(kB, 3)); + if (MB > 1) str_size = i18n("%1 MB").arg(TDEGlobal::locale()->formatNumber(MB, 3)); + if (GB > 1) str_size = i18n("%1 GB").arg(TDEGlobal::locale()->formatNumber(GB, 3)); + m_labelSize->setText(str_size); + + m_labelRate->setText(i18n("%1 Hz").arg(sf.m_SampleRate)); + return true; + } + return false; +} + + +void RecordingMonitor::slotStreamSelected(int idx) +{ + SoundStreamID old_id = m_currentStream; + if (old_id.isValid()) { + sendStopCapture(old_id); + } + + SoundStreamID id = m_idx2SoundStreamID.contains(idx) ? m_idx2SoundStreamID[idx] : SoundStreamID::InvalidID; + if (id.isValid()) { + + m_defaultStreamDescription = m_comboSoundStreamSelector->text(idx); + + SoundFormat sf; + sendStartCaptureWithFormat(id, sf, sf); + m_dataMonitor ->setEnabled(true); + m_labelSize ->setEnabled(true); + m_labelSize ->setEnabled(true); + m_labelTime ->setEnabled(true); + m_labelRate ->setEnabled(true); + m_labelFileName ->setEnabled(true); + m_labelStatus ->setEnabled(true); + } else { + m_dataMonitor ->setEnabled(false); + m_labelSize ->setEnabled(false); + m_labelSize ->setEnabled(false); + m_labelTime ->setEnabled(false); + m_labelRate ->setEnabled(false); + m_labelFileName ->setEnabled(false); + m_labelStatus ->setEnabled(false); + } + m_currentStream = id; + m_recording = false; + SoundFormat sf; + queryIsRecordingRunning(m_currentStream, m_recording, sf); + updateRecordingButton(); +} + + +void RecordingMonitor::updateRecordingButton() +{ + if (m_currentStream.isValid()) { + m_btnStartStop->setText(!m_recording ? i18n("&Record") : i18n("&Stop Recording")); + m_btnStartStop->setEnabled(true); + } else { + m_btnStartStop->setText(i18n("&Record")); + m_btnStartStop->setEnabled(false); + } +} + + +#include "recording-monitor.moc" diff --git a/tderadio3/plugins/recording/recording-monitor.h b/tderadio3/plugins/recording/recording-monitor.h new file mode 100644 index 0000000..ecc38f2 --- /dev/null +++ b/tderadio3/plugins/recording/recording-monitor.h @@ -0,0 +1,125 @@ +/*************************************************************************** + recording-monitor.h - description + ------------------- + begin : Mo Sep 1 2003 + copyright : (C) 2003 by Martin Witte + email : witte@kawo1.rwth-aachen.de + ***************************************************************************/ + +/*************************************************************************** + * * + * 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. * + * * + ***************************************************************************/ + +#ifndef KRADIO_RECORDING_MONITOR_H +#define KRADIO_RECORDING_MONITOR_H + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <tqwidget.h> + +#include "../../src/include/widgetplugins.h" +#include "../../src/include/soundstreamclient_interfaces.h" +//#include <kradio/interfaces/recording-interfaces.h> + + +class TQLabel; +class TQPushButton; +class TQCheckBox; +class RecordingDataMonitor; +class KComboBox; + +class RecordingMonitor : public TQWidget, + public WidgetPluginBase, + public ISoundStreamClient + //public IRecordingClient +{ +Q_OBJECT + +public: + + RecordingMonitor(const TQString &name); + virtual ~RecordingMonitor(); + + const TQString &name() const { return PluginBase::name(); } + TQString &name() { return PluginBase::name(); } + + virtual TQString pluginClassName() const { return "RecordingMonitor"; } + + // WidgetPluginBase + +public: + virtual void saveState (TDEConfig *) const; + virtual void restoreState (TDEConfig *); + + virtual bool connectI(Interface *i); + virtual bool disconnectI(Interface *i); + + virtual ConfigPageInfo createConfigurationPage(); + virtual AboutPageInfo createAboutPage(); + + // IRecordingClient + + void noticeConnectedI (ISoundStreamServer *s, bool pointer_valid); + + bool noticeSoundStreamCreated(SoundStreamID id); + bool noticeSoundStreamClosed(SoundStreamID id); + bool noticeSoundStreamChanged(SoundStreamID id); + + bool startRecordingWithFormat(SoundStreamID id, const SoundFormat &sf, SoundFormat &real_format); + bool stopRecording(SoundStreamID id); + + bool noticeSoundStreamData(SoundStreamID id, const SoundFormat &sf, const char *data, size_t size, size_t &consumed_size, const SoundMetaData &md); + +public slots: + + void toggleShown() { WidgetPluginBase::pToggleShown(); } + void showOnOrgDesktop(); + void show(); + void hide(); + + void slotStartStopRecording(); + + void slotStreamSelected(int idx); + +protected: + + virtual void updateRecordingButton(); + + virtual void showEvent(TQShowEvent *); + virtual void hideEvent(TQHideEvent *); + + const TQWidget *getWidget() const { return this; } + TQWidget *getWidget() { return this; } + + +protected: + + TQLabel *m_labelSize; + TQLabel *m_labelTime; + TQLabel *m_labelRate; + TQLabel *m_labelFileName; + TQLabel *m_labelStatus; + TQPushButton *m_btnStartStop; + + KComboBox *m_comboSoundStreamSelector; + TQMap<SoundStreamID, int> m_SoundStreamID2idx; + TQMap<int, SoundStreamID> m_idx2SoundStreamID; + + SoundStreamID m_currentStream; + RecordingDataMonitor *m_dataMonitor; + + bool m_recording; + TQString m_defaultStreamDescription; +}; + + + + +#endif 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 + email : witte@kawo1.rwth-aachen.de + ***************************************************************************/ + +/*************************************************************************** + * * + * 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", "", "witte@kawo1.rwth-aachen.de"); + + 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" diff --git a/tderadio3/plugins/recording/recording.h b/tderadio3/plugins/recording/recording.h new file mode 100644 index 0000000..7d48331 --- /dev/null +++ b/tderadio3/plugins/recording/recording.h @@ -0,0 +1,149 @@ +/*************************************************************************** + recording.h - description + ------------------- + begin : Mi Aug 27 2003 + copyright : (C) 2003 by Martin Witte + email : witte@kawo1.rwth-aachen.de + ***************************************************************************/ + +/*************************************************************************** + * * + * 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. * + * * + ***************************************************************************/ + +#ifndef KRADIO_RECORDING_H +#define KRADIO_RECORDING_H + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + + +#include <tqobject.h> +#include <tqstring.h> +#include <tqmap.h> + +#include "../../src/include/plugins.h" +#include "../../src/include/timecontrol_interfaces.h" +#include "../../src/include/soundstreamclient_interfaces.h" + +#include "recording-config.h" +#include "reccfg_interfaces.h" +#include "encoder.h" + +class RadioStation; +class StationList; +class TQSocketNotifier; +class RecordingEncoding; +class FileRingBuffer; + +class Recording : public TQObject, + public PluginBase, + public ISoundStreamClient, + public IRecCfg +{ +Q_OBJECT + +public: + Recording(const TQString &name); + ~Recording(); + + virtual TQString pluginClassName() const { return "Recording"; } + + virtual const TQString &name() const { return PluginBase::name(); } + virtual TQString &name() { return PluginBase::name(); } + + virtual bool connectI(Interface *i); + virtual bool disconnectI(Interface *i); + + + bool isRecording () const; + + + // PluginBase + +public: + virtual void saveState (TDEConfig *) const; + virtual void restoreState (TDEConfig *); + + virtual ConfigPageInfo createConfigurationPage(); + virtual AboutPageInfo createAboutPage(); + +protected: + +// IRecCfg + + bool setEncoderBuffer (size_t BufferSize, size_t BufferCount); + bool setSoundFormat (const SoundFormat &sf); + bool setMP3Quality (int q); + bool setOggQuality (float q); + bool setRecordingDirectory(const TQString &dir); + bool setOutputFormat (RecordingConfig::OutputFormat of); + bool setPreRecording (bool enable, int seconds); + bool setRecordingConfig (const RecordingConfig &cfg); + + void getEncoderBuffer(size_t &BufferSize, size_t &BufferCount) const; + const SoundFormat &getSoundFormat () const; + int getMP3Quality () const; + float getOggQuality () const; + const TQString &getRecordingDirectory() const; + RecordingConfig::OutputFormat getOutputFormat() const; + bool getPreRecording(int &seconds) const; + const RecordingConfig &getRecordingConfig() const; + +// ISoundStreamClient + + void noticeConnectedI (ISoundStreamServer *s, bool pointer_valid); + + bool startPlayback(SoundStreamID id); + bool stopPlayback(SoundStreamID id); + + bool startRecording(SoundStreamID id); + bool startRecordingWithFormat(SoundStreamID id, const SoundFormat &sf, SoundFormat &real_format); + bool noticeSoundStreamData(SoundStreamID id, const SoundFormat &sf, const char *data, size_t size, size_t &consumed_size, const SoundMetaData &md); + bool stopRecording(SoundStreamID id); + bool isRecordingRunning(SoundStreamID id, bool &b, SoundFormat &sf) const; + + bool getSoundStreamDescription(SoundStreamID id, TQString &descr) const; + bool getSoundStreamRadioStation(SoundStreamID id, const RadioStation *&rs) const; + + bool noticeSoundStreamClosed(SoundStreamID id); + bool noticeSoundStreamChanged(SoundStreamID id); + + bool enumerateSoundStreams(TQMap<TQString, SoundStreamID> &list) const; + +protected slots: + + bool event(TQEvent *e); + +protected: + + bool startEncoder(SoundStreamID ssid, const RecordingConfig &cfg); + void stopEncoder(SoundStreamID ssid); + +protected: + + RecordingConfig m_config; + TQMap<SoundStreamID, FileRingBuffer*> m_PreRecordingBuffers; + + TQMap<SoundStreamID, RecordingEncoding*> m_EncodingThreads; + TQMap<SoundStreamID, SoundStreamID> m_RawStreams2EncodedStreams; + TQMap<SoundStreamID, SoundStreamID> m_EncodedStreams2RawStreams; +}; + +/* PreRecording Notes: listen for startplayback, stopplayback, closestream + manage map streamid => buffer + set each started stream into capture mode + put data into ringbuffers + on capture start, feed everything into the encoder buffer, + if encoderbuffer < prerecbuffer => + put as much as possible into encoder + put new audio data into ring buffer + +*/ + +#endif diff --git a/tderadio3/plugins/recording/soundstreamevent.h b/tderadio3/plugins/recording/soundstreamevent.h new file mode 100644 index 0000000..c7b986e --- /dev/null +++ b/tderadio3/plugins/recording/soundstreamevent.h @@ -0,0 +1,87 @@ +/*************************************************************************** + soundstreamevent.h - description + ------------------- + begin : Fri May 06 2005 + copyright : (C) 2005 by Martin Witte + email : witte@kawo1.rwth-aachen.de + ***************************************************************************/ + +/*************************************************************************** + * * + * 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. * + * * + ***************************************************************************/ + +#ifndef KRADIO_RECORDING_SOUNDSTREAM_EVENT_H +#define KRADIO_RECORDING_SOUNDSTREAM_EVENT_H + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <tqevent.h> + +#include "../../src/include/sound_metadata.h" + +const TQEvent::Type EncodingTerminated = (TQEvent::Type)(TQEvent::User+1); +const TQEvent::Type EncodingStep = (TQEvent::Type)(TQEvent::User+2); + +class SoundStreamEvent : public TQEvent +{ +public: + SoundStreamEvent(TQEvent::Type t, SoundStreamID id) : TQEvent(t), m_SSID(id) {} + const SoundStreamID &getSoundStreamID() const { return m_SSID; } + + static bool isSoundStreamEvent (const TQEvent *e) { return e && ((e->type() == EncodingTerminated) || (e->type() == EncodingStep)); } + +protected: + SoundStreamID m_SSID; +}; + + + + + + +class SoundStreamEncodingTerminatedEvent : public SoundStreamEvent +{ +public: + SoundStreamEncodingTerminatedEvent(SoundStreamID id) : SoundStreamEvent(EncodingTerminated, id) {} +}; + + + + + + +class SoundStreamEncodingStepEvent : public SoundStreamEvent +{ +public: + SoundStreamEncodingStepEvent(SoundStreamID id, const char *data, size_t size, const SoundMetaData &md) + : SoundStreamEvent(EncodingStep, id), + m_Size(size), + m_MetaData(md) + { + m_Data = new char [m_Size]; + memcpy (m_Data, data, m_Size); + } + virtual ~SoundStreamEncodingStepEvent() { freeData(); } + + void freeData() { if (m_Data) delete m_Data; m_Data = NULL; m_Size = 0; } // _MUST_ be called by event receiver + + const char *data() const { return m_Data; } + size_t size() const { return m_Size; } + const SoundMetaData &metaData() const { return m_MetaData; } + + static bool isSoundStreamEncodingStep (const TQEvent *e) { return e && (e->type() == EncodingStep); } + +protected: + char *m_Data; + size_t m_Size; + SoundMetaData m_MetaData; +}; + +#endif |