diff options
Diffstat (limited to 'krecord.cpp')
-rw-r--r-- | krecord.cpp | 658 |
1 files changed, 658 insertions, 0 deletions
diff --git a/krecord.cpp b/krecord.cpp new file mode 100644 index 0000000..fc31bbe --- /dev/null +++ b/krecord.cpp @@ -0,0 +1,658 @@ +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <fcntl.h> +#include <sys/ioctl.h> + +#include <iostream> + +#include <qlabel.h> +#include <qfiledialog.h> +#include <qdragobject.h> +#include <qtooltip.h> +#include <qlayout.h> +#include <qbuttongroup.h> +#include <qhbuttongroup.h> +#include <qradiobutton.h> +#include <qaccel.h> +#include <qmenubar.h> +#include <qpopupmenu.h> + +#include <kapp.h> +#include <kuniqueapp.h> +#include <kmessagebox.h> +#include <kmenubar.h> +#include <kstatusbar.h> +#include <kmainwindow.h> +#include <kiconloader.h> +#include <klocale.h> +#include <kprocess.h> +#include <kaboutdata.h> +#include <kcmdlineargs.h> +#include <khelpmenu.h> +#include <kmenubar.h> +#include <kpopupmenu.h> + +#include "sound.h" +#include "fft.h" +#include "level.h" +#include "buffer.h" +#include "krecord.moc" + +#include <X11/Xlib.h> +#include <X11/Xatom.h> +#include <X11/Xmu/WinUtil.h> /* for XmuClientWindow() */ + +#define STAT_LATENCY 1 +#define STAT_RATE 2 +#define STAT_CHANNELS 3 +#define STAT_FORMAT 4 +#define STAT_MISC 5 + +KApplication *globalKapp; +KIconLoader *globalKIL; +KLocale *globalKlocale; +const char *device = NULL; + +/* ------------------------------------------------------------------------ */ + +int main(int argc, char **argv) +{ + static KCmdLineOptions options[] = { + { "d", 0, 0 }, + { "device <dev>", "dsp sound device", "/dev/dsp" }, + { "+[files]", "optional wav files", 0 }, + KCmdLineLastOption + }; + + KCmdLineArgs *args; + KRecord *krecord; + int i; + + KAboutData aboutData("krecord", "KRecord", KRECORD_VERSION, + "KDE sound recorder", + KAboutData::License_GPL, + "(c) 1997-2003 Gerd Knorr <kraxel@bytesex.org>\n", + 0, + "http://bytesex.org/krecord.html"); + aboutData.addAuthor("Gerd Knorr","Maintainer & Developer", + "kraxel@bytesex.org", "http://bytesex.org"); + aboutData.addCredit("Thomas Strehl", "KDE2 port", "tstrehl@suse.de"); + + KCmdLineArgs::init(argc, argv, &aboutData); + KCmdLineArgs::addCmdLineOptions(options); + + globalKapp = new KApplication("krecord"); + globalKIL = KGlobal::iconLoader(); + globalKlocale = KGlobal::locale(); + + args = KCmdLineArgs::parsedArgs(); + if (args->isSet("device")) + device = args->getOption("device"); + + krecord = new KRecord(); + for (i = 0; i < args->count(); i++) { + fprintf(stderr,"file: %s\n",args->arg(i)); + krecord->blist->add_filebuffer(args->arg(i)); + } + args->clear(); + + return globalKapp->exec(); +} + +/* ------------------------------------------------------------------------ */ + +KRecord::KRecord() : KMainWindow(0,"main") +{ + int i = -1; + + soundcard = new Soundcard(device); + soundopts = new SoundOptions(soundcard,"soundopts"); + kfft = new KFFT(soundcard); + klevel = new KLevel(soundcard); + listwidget = new QListBox(this,"bufferlist"); + blist = new BufferList(listwidget,soundcard); + fdialog = new QFileDialog(NULL,"*.wav",NULL,"fdialog",TRUE); + accel = new QAccel(this); + + globalKapp->setMainWidget(this); + setCentralWidget(listwidget); + setAcceptDrops(TRUE); + + create_menu(); + create_toolbar(); + create_soundbar(); + create_statusline(); + soundopts->set_soundparam(44100,2,FMT_16BIT,0); + + accel->connectItem(accel->insertItem(Key_Enter), blist,SLOT(play())); + accel->connectItem(accel->insertItem(Key_Return), blist,SLOT(play())); + accel->connectItem(accel->insertItem(Key_Escape), blist,SLOT(stop())); + accel->connectItem(accel->insertItem(Key_Delete), blist,SLOT(del_buf())); + accel->connectItem(accel->insertItem(Key_R), blist,SLOT(record())); + accel->connectItem(accel->insertItem(Key_N), + blist,SLOT(next_buffer())); + accel->connectItem(accel->insertItem(Key_Space), + blist,SLOT(next_buffer())); + + connect(soundcard,SIGNAL(newparams(struct SOUNDPARAMS*)), + this, SLOT(update_statusline(struct SOUNDPARAMS*))); + connect(blist,SIGNAL(status(const char*)), + this, SLOT(update_statusline(const char*))); + connect(soundopts,SIGNAL(set_level(int)), + blist, SLOT(set_level(int))); + + /* session management */ + if (globalKapp->isRestored()) { + for (i = 1; canBeRestored(i); i++) + if (0 == strcmp(classNameOfToplevel(i),"KRecord")) + break; + if (!canBeRestored(i)) + i = -1; + } + if (i > 0) { + restore(i); + } else { + resize(400,250); + show(); + } + + blist->monitor(); +} + +KRecord::~KRecord() +{ + delete opt_menu; + delete file_menu; + + delete toolbar; + delete soundbar; + delete statusline; + + delete blist; + delete listwidget; + + delete fdialog; + delete kfft; + delete soundopts; + delete soundcard; +} + +void +KRecord::create_menu() +{ + file_menu = new QPopupMenu; + file_menu->insertItem( i18n("&New memory buffer"), + blist, SLOT(new_ram())); + file_menu->insertItem( i18n("New &file buffer..."), + this, SLOT(new_file())); + file_menu->insertItem( i18n("&Save buffer as..."), + this, SLOT(save_as())); + file_menu->insertSeparator(); + file_menu->insertItem( i18n("&Delete buffer"), + blist, SLOT(del_buf())); + file_menu->insertSeparator(); + file_menu->insertItem( i18n("&Quit"), + this, SLOT(quit_cb()), CTRL+Key_Q); + + opt_menu = new QPopupMenu; + opt_menu->insertItem( i18n("&Sound Options..."), + this, SLOT(record_options())); + opt_menu->insertItem( i18n("&Freq Spectrum..."), + kfft, SLOT(showit())); + opt_menu->insertItem( i18n("&Input Level..."), + klevel, SLOT(showit())); + opt_menu->insertItem( i18n("Run &Mixer"), + this, SLOT(exec_mixer())); + opt_menu->insertSeparator(); + tb_mid = opt_menu->insertItem( i18n("Hide &Toolbar"), + this,SLOT(tb_toggle())); + sl_mid = opt_menu->insertItem( i18n("Hide Status&line"), + this,SLOT(sl_toggle())); + + KPopupMenu *help = helpMenu(); + + menuBar()->insertItem( i18n("&File"), file_menu); + menuBar()->insertItem( i18n("&Options"), opt_menu); + menuBar()->insertItem( i18n("&Help"), help); +} + +void +KRecord::create_toolbar() +{ + toolbar = new KToolBar(this,"Toolbar"); + KIconLoader *loader = KGlobal::iconLoader(); + QPixmap pixmap; + + pixmap = loader->loadIcon("filenew",KIcon::Toolbar); + toolbar->insertButton + (pixmap, 0, SIGNAL(clicked()), blist, SLOT(new_ram()), TRUE, + i18n("New memory buffer")); + + pixmap = loader->loadIcon("filesave",KIcon::Toolbar); + toolbar->insertButton + (pixmap, 0, SIGNAL(clicked()), this, SLOT(save_as()), TRUE, + i18n("Save buffer")); + + toolbar->insertSeparator(); + + pixmap = loader->loadIcon("freq",KIcon::Toolbar); + toolbar->insertButton + (pixmap, 0, SIGNAL(clicked()), kfft, SLOT(showit()), TRUE, + i18n("Freq Spectrum")); + + pixmap = loader->loadIcon("level",KIcon::Toolbar); + toolbar->insertButton + (pixmap, 0, SIGNAL(clicked()), klevel, SLOT(showit()), TRUE, + i18n("Input Level")); + + toolbar->insertSeparator(); + +#if 0 + pixmap = loader->loadIcon("help",KIcon::Toolbar,10); + toolbar->insertButton + (pixmap, 0, SIGNAL(clicked()), this, SLOT(help_cb()), TRUE, + i18n("Help")); +#endif + + toolbar->insertSeparator(); + pixmap = loader->loadIcon("exit",KIcon::Toolbar); + toolbar->insertButton + (pixmap, 0, SIGNAL(clicked()), this, SLOT(quit_cb()), TRUE, + i18n("Quit")); + + toolbar->setBarPos(KToolBar::Top); + addToolBar(toolbar); +} + +void +KRecord::create_soundbar() +{ + soundbar = new KToolBar(this,"Soundbar"); + KIconLoader *loader = KGlobal::iconLoader(); + QPixmap pixmap; + + pixmap = loader->loadIcon("forward",KIcon::Toolbar,10); + soundbar->insertButton + (pixmap, 0, SIGNAL(clicked()), blist, SLOT(next_buffer()), TRUE, + i18n("Switch to new buffer")); + + pixmap = loader->loadIcon("mrecord",KIcon::Toolbar); + soundbar->insertButton + (pixmap, 0, SIGNAL(clicked()), blist, SLOT(record()), TRUE, + i18n("Start Record")); + + pixmap = loader->loadIcon("player_stop",KIcon::Toolbar); + soundbar->insertButton + (pixmap, 0, SIGNAL(clicked()), blist, SLOT(stop()), TRUE, + i18n("Stop Record/Playback")); + + pixmap = loader->loadIcon("1rightarrow",KIcon::Toolbar); + soundbar->insertButton + (pixmap, 0, SIGNAL(clicked()), blist, SLOT(play()), TRUE, + i18n("Start Playback")); + + pixmap = loader->loadIcon("2leftarrow",KIcon::Toolbar); + soundbar->insertButton + (pixmap, 0, SIGNAL(clicked()), blist, SLOT(backward()), TRUE, + i18n("Back")); + + pixmap = loader->loadIcon("2rightarrow",KIcon::Toolbar); + soundbar->insertButton + (pixmap, 0, SIGNAL(clicked()), blist, SLOT(forward()), TRUE, + i18n("Forward")); + + soundbar->insertSeparator(); + pixmap = loader->loadIcon("line_monitor",KIcon::Toolbar); + soundbar->insertButton + (pixmap, 0, SIGNAL(clicked()), blist, SLOT(monitor()), TRUE, + i18n("Turn on/off monitor")); + + soundbar->setBarPos(KToolBar::Top); + addToolBar(soundbar); +} + +void +KRecord::create_statusline() +{ + statusline = new KStatusBar(this); + + statusline->insertItem("-", STAT_MISC, 1); + statusline->setItemAlignment (STAT_MISC, AlignLeft); + statusline->insertFixedItem("9999999", STAT_RATE); + statusline->insertFixedItem("xxxxxxx", STAT_CHANNELS); + statusline->insertFixedItem("xxxxxxxxxx", STAT_FORMAT); + statusline->insertFixedItem("999 ms", STAT_LATENCY); +} + +void +KRecord::update_statusline(struct SOUNDPARAMS *p) +{ + char text[32]; + + statusline->changeItem((1==p->channels) ? "mono":"stereo", STAT_CHANNELS); + sprintf(text,"%d",p->rate); + statusline->changeItem(text,STAT_RATE); + sprintf(text,"%d ms",p->latency); + statusline->changeItem(text,STAT_LATENCY); + statusline->changeItem(sndfmt2str(p->format),STAT_FORMAT); +} + +void +KRecord::update_statusline(const char *text) +{ + statusline->changeItem(text,STAT_MISC); +} + + +/* ------------------------------------------------------------------------ */ + +void KRecord::new_file() +{ + QString filename; + + if (NULL == (filename = fdialog->getSaveFileName + (NULL,"*.wav",NULL,"fdialog"))) + return; + blist->add_filebuffer(filename); +} + +void KRecord::save_as() +{ + QString filename; + + if (NULL == (filename = fdialog->getSaveFileName + (NULL,"*.wav",NULL,"fdialog"))) + return; + blist->save_buf(filename); +} + +void KRecord::quit_cb() +{ + blist->stop(); + delete this; + globalKapp->quit(); +} + +void KRecord::record_options() +{ + soundopts->show(); +} + +void KRecord::exec_mixer() +{ + KProcess *kmix; + kmix = new KProcess; + *kmix << "kmix"; + kmix->start(KProcess::DontCare); +} + +void KRecord::tb_toggle() +{ + if (toolBar("Toolbar")->isVisible()) { + toolBar("Toolbar")->enable(KToolBar::Hide); + opt_menu->changeItem(i18n("Show &Toolbar"), tb_mid); + } else { + toolBar("Toolbar")->enable(KToolBar::Show); + opt_menu->changeItem(i18n("Hide &Toolbar"), tb_mid); + } +} + +void KRecord::sl_toggle() +{ + if (statusBar()->isVisible()) { + statusBar()->hide(); + opt_menu->changeItem(i18n("Show Status&line"), sl_mid); + } else { + statusBar()->show(); + opt_menu->changeItem(i18n("Hide Status&line"), sl_mid); + } +} + +void KRecord::dropEvent(QDropEvent *de) // something has been dropped +{ + QStrList strlist; + QUriDrag::decode(de,strlist); + QString *url = new QString(strlist.first()); + const char *h; + + fprintf(stderr,"dropEvent\n"); + while ((const char*)*url) { + h = (const char*)*url; + if (0 == strncmp(h,"file:",5)) + blist->add_filebuffer(h+5); + delete url; + url = new QString(strlist.next()); + } +} + +void KRecord::dragEnterEvent(QDragEnterEvent* event) +{ + event->accept(QTextDrag::canDecode(event) || + QImageDrag::canDecode(event)); +} + +/* ------------------------------------------------------------------------ */ + +KFFT::KFFT(Soundcard *card) : KMainWindow(0,"fft",WType_TopLevel) +{ + int i = -1; + + QWidget* centralWidget = new QWidget(this); + setCentralWidget(centralWidget); + + QVBoxLayout *topLayout = new QVBoxLayout(centralWidget,0); + + /* button group #1 */ + QButtonGroup* linLogGroup = new QHButtonGroup( centralWidget, "LinLogGroup" ); + topLayout->addWidget(linLogGroup,0); + + QRadioButton* rb1 = new QRadioButton( linLogGroup ); + rb1->setText( i18n("L&og") ); + QToolTip::add( rb1, i18n("Logarithmic Y scale") ); + + QRadioButton* rb2 = new QRadioButton( linLogGroup ); + rb2->setText( i18n("L&inear") ); + rb2->setChecked( TRUE ); + QToolTip::add( rb2, i18n("Linear Y scale") ); + + fftwin = new FFTWindow(centralWidget,"fft"); + topLayout->addWidget(fftwin,1); + QObject::connect(card,SIGNAL(senddata(void*)), + fftwin, SLOT(new_data(void*))); + QObject::connect(card,SIGNAL(newparams(struct SOUNDPARAMS*)), + fftwin, SLOT(new_params(struct SOUNDPARAMS*))); + QObject::connect(linLogGroup, SIGNAL(clicked(int)), + fftwin, SLOT(set_ylog(int)) ); + + setCaption("freq spectrum"); + +#if 1 + /* session management */ + if (globalKapp->isRestored()) { + for (i = 1; canBeRestored(i); i++) + if (0 == strcmp(classNameOfToplevel(i),"KFFT")) + break; + if (!canBeRestored(i)) + i = -1; + } + if (i > 0) { + restore(i); + } else { + resize(200,120); + } +#else + resize(200,120); +#endif +} + +KFFT::~KFFT() +{ + delete fftwin; + fftwin = NULL; +} + +void +KFFT::showit() +{ + if (!isVisible()) + show(); +} + +/* ------------------------------------------------------------------------ */ + +KLevel::KLevel(Soundcard *card) : KMainWindow(0,"level",WType_TopLevel) +{ + QRadioButton *rb1; + QRadioButton *rb2; + + thislevelwidget = new QWidget(this); + setCentralWidget(thislevelwidget); + + QVBoxLayout *topLayout = new QVBoxLayout(thislevelwidget,0); + QHBoxLayout *hbox = new QHBoxLayout(); + topLayout->addLayout(hbox); + + /* button group #1 */ + PowMaxGroup = new QButtonGroup( thislevelwidget, "PowMaxGroup" ); + QHBoxLayout *vbox = new QHBoxLayout(PowMaxGroup); + hbox->addWidget(PowMaxGroup); + + rb1 = new QRadioButton( PowMaxGroup ); + rb1->setText( i18n("L&og") ); + rb1->setChecked( TRUE ); + vbox->addWidget(rb1); + rb1->setMinimumSize(rb1->sizeHint()); + QToolTip::add( rb1, i18n("Logarithmic scale") ); + + rb2 = new QRadioButton( PowMaxGroup ); + rb2->setText( i18n("L&inear") ); + vbox->addWidget(rb2); + rb2->setMinimumSize( rb2->sizeHint() ); + QToolTip::add( rb2, i18n("Linear scale") ); + + connect( PowMaxGroup, SIGNAL(clicked(int)), SLOT(LogvsLinearClicked(int))); + + /* button group #2 */ + LogLinGroup = new QButtonGroup( thislevelwidget, "LogLinGroup" ); + vbox = new QHBoxLayout(LogLinGroup, 2); + hbox->addWidget( LogLinGroup, 0); + + rb1 = new QRadioButton( LogLinGroup ); + rb1->setText( i18n("&Power") ); + rb1->setChecked( TRUE ); + vbox->addWidget(rb1); + rb1->setMinimumSize( rb1->sizeHint() ); + QToolTip::add( rb1, i18n("Display power carried by signal") ); + + rb2 = new QRadioButton( LogLinGroup ); + rb2->setText( i18n("&Max") ); + vbox->addWidget(rb2); + rb2->setMinimumSize( rb2->sizeHint() ); + QToolTip::add( rb2, i18n("Display signal maximum level") ); + + + /* level window */ + levelwin = new LevelWindow(thislevelwidget,"level"); + levelwin->setMinimumSize(40,10); + topLayout->addWidget(levelwin, 1); + + QObject::connect(card,SIGNAL(senddata(void*)), + levelwin, SLOT(new_data(void*))); + QObject::connect(card,SIGNAL(newparams(struct SOUNDPARAMS*)), + levelwin, SLOT(new_params(struct SOUNDPARAMS*))); + setCaption("input level"); + + + /* scale */ + hbox = new QHBoxLayout(); + topLayout->addLayout(hbox); + llabel = new QLabel(thislevelwidget,"llabel"); + llabel->setText("-100 dB"); + llabel->setAlignment(AlignLeft); + llabel->setMinimumSize(llabel->sizeHint()); + mlabel = new QLabel(thislevelwidget,"mlabel"); + mlabel->setText("-"); + mlabel->setAlignment(AlignCenter); + mlabel->setMinimumSize(mlabel->sizeHint()); + rlabel = new QLabel(thislevelwidget,"rlabel"); + rlabel->setText("0 dB"); + rlabel->setAlignment(AlignRight); + rlabel->setMinimumSize(llabel->sizeHint()); + hbox->addWidget(llabel,1); + hbox->addWidget(mlabel,1); + hbox->addWidget(rlabel,1); + + connect(LogLinGroup, SIGNAL(clicked(int)), SLOT(PowervsMaxClicked(int))); + + connect(levelwin,SIGNAL(setvalue(char*)), + this,SLOT(setvalue(char*))); + + /* show it */ + setMaximumSize(800,80); + resize(320,100); + topLayout->activate(); +} + +KLevel::~KLevel() +{ + delete thislevelwidget; + thislevelwidget = NULL; + delete levelwin; + levelwin = NULL; +} + +void +KLevel::showit() +{ + if (!isVisible()) + show(); +} + +void +KLevel::resizeEvent( QResizeEvent * ) +{ + thislevelwidget->resize(size()); + thislevelwidget->show(); +} + +void +KLevel::setvalue(char *text) { + mlabel->setText(text); +} + +void +KLevel::updatelabels() { + if (levelwin->PowervsMax) { + if (levelwin->LogvsLinear) { + llabel->setText("-100 dB"); + rlabel->setText("0 dB"); + } else { + llabel->setText(""); + rlabel->setText(""); + } + } else { + if (levelwin->LogvsLinear) { + llabel->setText("-50 dB"); + rlabel->setText("0 dB"); + } else { + llabel->setText("0%"); + rlabel->setText("100%"); + } + } + mlabel->setText(""); +} + +void +KLevel::PowervsMaxClicked(int i) { + if (i==0) + levelwin->PowervsMax=true; else levelwin->PowervsMax=false; + updatelabels(); +}; + +void +KLevel::LogvsLinearClicked(int i) { + if (i==0) + levelwin->LogvsLinear=1; else levelwin->LogvsLinear=0; + updatelabels(); +}; |