Main Page | Namespace List | Class Hierarchy | Alphabetical List | Compound List | File List | Compound Members | File Members

XMMSOut.cc

Go to the documentation of this file.
00001 /*
00002 
00003     XMMS output plugin streamdist replacement
00004     Copyright (C) 2003 Jussi Laako
00005 
00006     This program is free software; you can redistribute it and/or modify
00007     it under the terms of the GNU General Public License as published by
00008     the Free Software Foundation; either version 2 of the License, or
00009     (at your option) any later version.
00010 
00011     This program is distributed in the hope that it will be useful,
00012     but WITHOUT ANY WARRANTY; without even the implied warranty of
00013     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00014     GNU General Public License for more details.
00015 
00016     You should have received a copy of the GNU General Public License
00017     along with this program; if not, write to the Free Software
00018     Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
00019 
00020 */
00021 
00022 
00023 #include <stdio.h>
00024 #include <stdlib.h>
00025 #include <string.h>
00026 #include <math.h>
00027 #include <float.h>
00028 #include <limits.h>
00029 #include <signal.h>
00030 #include <unistd.h>
00031 #include <sys/time.h>
00032 
00033 #include "XMMSOut.hh"
00034 
00035 
00036 extern "C"
00037 {
00038 OutputPlugin *get_oplugin_info (void);
00039 void hasas_init (void);
00040 void hasas_about (void);
00041 void hasas_configure (void);
00042 void hasas_get_volume (int *, int *);
00043 void hasas_set_volume (int, int);
00044 int hasas_open_audio (AFormat, int, int);
00045 void hasas_write_audio (void *, int);
00046 void hasas_close_audio (void);
00047 void hasas_flush (int);
00048 void hasas_pause (short);
00049 int hasas_buffer_free (void);
00050 int hasas_buffer_playing (void);
00051 int hasas_output_time (void);
00052 int hasas_written_time (void);
00053 }
00054 
00055 
00056 OutputPlugin hasas_op = {
00057     NULL,
00058     NULL,
00059     NULL,
00060     hasas_init,
00061     hasas_about,
00062     hasas_configure,
00063     NULL,
00064     NULL,
00065     hasas_open_audio,
00066     hasas_write_audio,
00067     hasas_close_audio,
00068     hasas_flush,
00069     hasas_pause,
00070     hasas_buffer_free,
00071     hasas_buffer_playing,
00072     hasas_output_time,
00073     hasas_written_time
00074 };
00075 clXMMSOut XMMSOut;
00076 clDynThreads<clXMMSOut> XMMSOutThreads(XMMSOut);
00077 static const int iDefBufferSize = 65536;
00078 static const char *cpDefLocalSocket = "streamdist.socket";
00079 /* UI component texts */
00080 static const char *cpWinTitleTxt = "HASAS streamdist settings";
00081 static const char *cpLBufSizeTxt = "Buffer size";
00082 static const char *cpLLocalSocketTxt = "Local socket";
00083 static const char *cpBOkTxt = "OK";
00084 static const char *cpBCancelTxt = "Cancel";
00085 
00086 
00087 extern "C"
00088 {
00089 
00090 
00091 OutputPlugin *get_oplugin_info()
00092 {
00093     hasas_op.description = g_strdup_printf("%s %i.%i.%i", 
00094         XMMSOUT_DESCRIPTION, 
00095         GLOBAL_VERSMAJ, GLOBAL_VERSMIN, GLOBAL_VERSPL);
00096     return &hasas_op;
00097 }
00098 
00099 
00100 void hasas_init ()
00101 {
00102     XMMSOut.Init();
00103 }
00104 
00105 
00106 void hasas_about ()
00107 {
00108     XMMSOut.About();
00109 }
00110 
00111 
00112 void hasas_configure ()
00113 {
00114     XMMSOut.Configure();
00115 }
00116 
00117 
00118 int hasas_open_audio (AFormat fmt, int rate, int nch)
00119 {
00120     return XMMSOut.OpenAudio(fmt, rate, nch);
00121 }
00122 
00123 
00124 void hasas_write_audio (void *ptr, int length)
00125 {
00126     XMMSOut.WriteAudio(ptr, length);
00127 }
00128 
00129 
00130 void hasas_close_audio ()
00131 {
00132     XMMSOut.CloseAudio();
00133 }
00134 
00135 
00136 void hasas_flush (int time)
00137 {
00138     XMMSOut.Flush(time);
00139 }
00140 
00141 
00142 void hasas_pause (short paused)
00143 {
00144     XMMSOut.Pause(paused);
00145 }
00146 
00147 
00148 int hasas_buffer_free ()
00149 {
00150     return XMMSOut.BufferFree();
00151 }
00152 
00153 
00154 int hasas_buffer_playing ()
00155 {
00156     return XMMSOut.BufferPlaying();
00157 }
00158 
00159 
00160 int hasas_output_time ()
00161 {
00162     return XMMSOut.OutputTime();
00163 }
00164 
00165 
00166 int hasas_written_time ()
00167 {
00168     return XMMSOut.WrittenTime();
00169 }
00170 
00171 
00172 void WrapOnAboutButton (GtkButton *gbSender, gpointer gpData)
00173 {
00174     XMMSOut.OnAboutButton(gbSender, gpData);
00175 }
00176 
00177 
00178 void WrapOnButtonClick (GtkButton *gbSender, gpointer gpData)
00179 {
00180     XMMSOut.OnButtonClick(gbSender, gpData);
00181 }
00182 
00183 }
00184 
00185 
00186 inline double clXMMSOut::GetTime ()
00187 {
00188     double dTime;
00189     struct timeval sTime;
00190     
00191     gettimeofday(&sTime, NULL);
00192     dTime = (double) sTime.tv_sec + (double) sTime.tv_usec / 1.0e6;
00193     return dTime;
00194 }
00195 
00196 
00197 inline void clXMMSOut::Convert8s8u (void *vpData, int iCount)
00198 {
00199     int iSampleCntr;
00200     signed char *cpSrcData = (signed char *) vpData;
00201     unsigned char *cpDestData = (unsigned char *) vpData;
00202 
00203     for (iSampleCntr = 0; iSampleCntr < iCount; iSampleCntr++)
00204     {
00205         cpDestData[iSampleCntr] = (unsigned char)
00206             ((int) cpSrcData[iSampleCntr] + 0x7f);
00207     }
00208 }
00209 
00210 
00211 inline void clXMMSOut::Convert16u16s (void *vpData, int iCount)
00212 {
00213     int iSampleCntr;
00214     unsigned short *uipSrcData = (unsigned short *) vpData;
00215     signed short *ipDestData = (signed short *) vpData;
00216 
00217     for (iSampleCntr = 0; iSampleCntr < iCount; iSampleCntr++)
00218     {
00219         ipDestData[iSampleCntr] = (signed short)
00220             ((int) uipSrcData[iSampleCntr] - 0x7fff);
00221     }
00222 }
00223 
00224 
00225 inline void clXMMSOut::EndianConvert (unsigned short *uipData, int iCount)
00226 {
00227     int iSampleCntr;
00228     
00229     for (iSampleCntr = 0; iSampleCntr < iCount; iSampleCntr++)
00230     {
00231         //uipData[iSampleCntr] = GUINT16_SWAP_LE_BE(uipData[iSampleCntr]);
00232         uipData[iSampleCntr] = (
00233             ((uipData[iSampleCntr] & 0x00ff) << 8) |
00234             ((uipData[iSampleCntr] & 0xff00) >> 8));
00235     }
00236 }
00237 
00238 
00239 inline void clXMMSOut::CopyChannel (GDT *fpDest, const GDT *fpSrc, 
00240     int iChannel)
00241 {
00242     int iSampleCntr;
00243     int iSampleCount;
00244 
00245     iSampleCount = iFragmentSize / sHdr.iChannels;
00246     for (iSampleCntr = 0; iSampleCntr < iSampleCount; iSampleCntr++)
00247     {
00248         fpDest[iSampleCntr] = fpSrc[iSampleCntr * sHdr.iChannels + iChannel];
00249     }
00250 }
00251 
00252 
00253 clXMMSOut::clXMMSOut ()
00254 {
00255     bRun = false;
00256     bPause = false;
00257     iMainThreadH = -1;
00258     iAudioBufSize = 0;
00259     iFragmentSize = 0;
00260     dStartTime = 0.0;
00261     cpLocalSocket = NULL;
00262     gwWinConfig = NULL;
00263 }
00264 
00265 
00266 clXMMSOut::~clXMMSOut ()
00267 {
00268     CloseAudio();
00269 }
00270 
00271 
00272 void clXMMSOut::Init ()
00273 {
00274     int iBufSize;
00275     ConfigFile *cfCfgFile;
00276 
00277     cfCfgFile = xmms_cfg_open_default_file();
00278     if (cfCfgFile == NULL)
00279         cfCfgFile = xmms_cfg_new();
00280     iBufSize = iDefBufferSize;
00281     xmms_cfg_read_int(cfCfgFile, "hasas","buffersize", &iBufSize);
00282 //g_print("hasas_op: buffer size %i\n", iBufSize);
00283     iAudioBufSize = iBufSize;
00284     if (cpLocalSocket)
00285         g_free(cpLocalSocket);
00286     if (!xmms_cfg_read_string(cfCfgFile, "hasas", 
00287         "localsocket", &cpLocalSocket))
00288         cpLocalSocket = g_strdup(cpDefLocalSocket);
00289     xmms_cfg_free(cfCfgFile);
00290 
00291     iFragmentSize = iAudioBufSize / sizeof(GDT);
00292     AudioBuf.Size(iAudioBufSize);
00293     if (!SServ.Bind(cpLocalSocket))
00294         g_warning("hasas_op: failed to bind local socket %s", cpLocalSocket);
00295 }
00296 
00297 
00298 void clXMMSOut::About ()
00299 {
00300     cpMessageTxt = g_strdup_printf(
00301         "%s %i.%i.%i\nCopyright (C) 2003 Jussi Laako", 
00302         XMMSOUT_DESCRIPTION, 
00303         GLOBAL_VERSMAJ, GLOBAL_VERSMIN, GLOBAL_VERSPL);
00304     gwMessageBox = xmms_show_message("About plugin", cpMessageTxt, "OK", TRUE,
00305         GTK_SIGNAL_FUNC(WrapOnAboutButton), NULL);
00306 }
00307 
00308 
00309 void clXMMSOut::Configure ()
00310 {
00311     gchar *cpEntryTxt;
00312 
00313     if (gwWinConfig)
00314     {
00315         gdk_window_raise(gwWinConfig->window);
00316         return;
00317     }
00318 
00319     gwWinConfig = gtk_window_new(GTK_WINDOW_TOPLEVEL);
00320     gtk_window_set_title(GTK_WINDOW(gwWinConfig), cpWinTitleTxt);
00321     gtk_window_set_policy(GTK_WINDOW(gwWinConfig), FALSE, FALSE, FALSE);
00322     
00323     gwVBox = gtk_vbox_new(FALSE, 4);
00324     gtk_container_add(GTK_CONTAINER(gwWinConfig), gwVBox);
00325     gtk_widget_show(gwVBox);
00326     
00327     gwLBufSize = gtk_label_new(cpLBufSizeTxt);
00328     gtk_box_pack_start(GTK_BOX(gwVBox), gwLBufSize, FALSE, FALSE, 0);
00329     gtk_widget_show(gwLBufSize);
00330     
00331     gwEBufSize = gtk_entry_new();
00332     gtk_box_pack_start(GTK_BOX(gwVBox), gwEBufSize, FALSE, FALSE, 0);
00333     gtk_widget_show(gwEBufSize);
00334     cpEntryTxt = g_strdup_printf("%i", iAudioBufSize);
00335     gtk_entry_set_text(GTK_ENTRY(gwEBufSize), cpEntryTxt);
00336     g_free(cpEntryTxt);
00337     
00338     gwLLocalSocket = gtk_label_new(cpLLocalSocketTxt);
00339     gtk_box_pack_start(GTK_BOX(gwVBox), gwLLocalSocket, FALSE, FALSE, 0);
00340     gtk_widget_show(gwLLocalSocket);
00341     
00342     gwELocalSocket = gtk_entry_new();
00343     gtk_box_pack_start(GTK_BOX(gwVBox), gwELocalSocket, FALSE, FALSE, 0);
00344     gtk_widget_show(gwELocalSocket);
00345     cpEntryTxt = g_strdup_printf("%s", cpLocalSocket);
00346     gtk_entry_set_text(GTK_ENTRY(gwELocalSocket), cpEntryTxt);
00347     g_free(cpEntryTxt);
00348     
00349     gwHBox = gtk_hbox_new(TRUE, 4);
00350     gtk_box_pack_start(GTK_BOX(gwVBox), gwHBox, FALSE, FALSE, 0);
00351     gtk_widget_show(gwHBox);
00352     
00353     gwBOk = gtk_button_new_with_label(cpBOkTxt);
00354     gtk_box_pack_start(GTK_BOX(gwHBox), gwBOk, TRUE, TRUE, 0);
00355     gtk_widget_show(gwBOk);
00356     
00357     gwBCancel = gtk_button_new_with_label(cpBCancelTxt);
00358     gtk_box_pack_start(GTK_BOX(gwHBox), gwBCancel, TRUE, TRUE, 0);
00359     gtk_widget_show(gwBCancel);
00360     
00361     gtk_signal_connect(GTK_OBJECT(gwBOk), "clicked",
00362         GTK_SIGNAL_FUNC(WrapOnButtonClick), NULL);
00363     gtk_signal_connect(GTK_OBJECT(gwBCancel), "clicked",
00364         GTK_SIGNAL_FUNC(WrapOnButtonClick), NULL);
00365 
00366     gtk_widget_show(gwWinConfig);
00367 }
00368 
00369 
00370 int clXMMSOut::OpenAudio (AFormat eAudioFormatP, int iSampleRate, 
00371     int iChannels)
00372 {
00373 //g_print("hasas_op: format %i, samplerate %i, channels %i\n", eAudioFormatP, iSampleRate, iChannels);
00374     eAudioFormat = eAudioFormatP;
00375 
00376     if (iMainThreadH >= 0)
00377         return 0;
00378 
00379     sHdr.iChannels = iChannels;
00380     sHdr.dSampleRate = iSampleRate;
00381     iWriteTime = 0;
00382     iPlayTime = 0;
00383     uiTotalTickCount = 0;
00384     dStartTime = 0.0;
00385     bRun = true;
00386     iMainThreadH = XMMSOutThreads.Create(&clXMMSOut::MainThread, NULL);
00387 
00388     return 1;
00389 }
00390 
00391 
00392 void clXMMSOut::WriteAudio (void *vpData, int iSize) // iLength?
00393 {
00394     bool bData;
00395     int iSampleCount = 0;
00396     int iTickCount;
00397     double dFragTime;
00398     double dWaitTime;
00399     clAlloc LocalAudioBuf;
00400 
00401     if (dStartTime == 0.0)
00402         dStartTime = GetTime();
00403 
00404     switch (eAudioFormat)
00405     {
00406         case FMT_S8:
00407 //g_print("hasas_op: FMT_S8\n");
00408             Convert8s8u(vpData, iSize / sizeof(char));
00409         case FMT_U8:
00410 //g_print("hasas_op: FMT_U8\n");
00411             iSampleCount = iSize / sizeof(unsigned char);
00412             LocalAudioBuf.Size(iSampleCount * sizeof(GDT));
00413             DSP.Convert((GDT *) LocalAudioBuf, (unsigned char *) vpData, 
00414                 iSampleCount);
00415             break;
00416         case FMT_U16_LE:
00417 #           if (G_BYTE_ORDER == G_BIG_ENDIAN)
00418 //g_print("hasas_op: FMT_U16_LE\n");
00419             /*EndianConvert((unsigned short *) vpData, 
00420                 iSize / sizeof(unsigned short));*/
00421 #           endif
00422         case FMT_U16_BE:
00423 #           if (G_BYTE_ORDER == G_LITTLE_ENDIAN)
00424 //g_print("hasas_op: FMT_U16_BE\n");
00425             /*EndianConvert((unsigned short *) vpData, 
00426                 iSize / sizeof(unsigned short));*/
00427 #           endif
00428         case FMT_U16_NE:
00429 //g_print("hasas_op: FMT_U16_NE\n");
00430             Convert16u16s(vpData, iSize / sizeof(short));
00431             iSampleCount = iSize / sizeof(signed short);
00432             LocalAudioBuf.Size(iSampleCount * sizeof(GDT));
00433             DSP.Convert((GDT *) LocalAudioBuf, (signed short *) vpData,
00434                 iSampleCount, false);
00435             break;
00436         case FMT_S16_LE:
00437 #           if (G_BYTE_ORDER == G_BIG_ENDIAN)
00438 //g_print("hasas_op: FMT_S16_LE\n");
00439             /*EndianConvert((unsigned short *) vpData, 
00440                 iSize / sizeof(unsigned short));*/
00441 #           endif
00442         case FMT_S16_BE:
00443 #           if (G_BYTE_ORDER == G_LITTLE_ENDIAN)
00444 //g_print("hasas_op: FMT_S16_BE\n");
00445             /*EndianConvert((unsigned short *) vpData, 
00446                 iSize / sizeof(unsigned short));*/
00447 #           endif
00448         case FMT_S16_NE:
00449 //g_print("hasas_op: FMT_S16_NE\n");
00450             iSampleCount = iSize / sizeof(signed short);
00451             LocalAudioBuf.Size(iSampleCount * sizeof(GDT));
00452             DSP.Convert((GDT *) LocalAudioBuf, (signed short *) vpData,
00453                 iSampleCount, false);
00454             break;
00455     }
00456 
00457     iTickCount = iSampleCount / sHdr.iChannels;
00458     iWriteTime = (int) ((double) (uiTotalTickCount + iTickCount) / 
00459         sHdr.dSampleRate * 1000.0 + 0.5);
00460     dFragTime = (double) iTickCount / sHdr.dSampleRate;
00461 //g_print("hasas_op: frag time %f\n", dFragTime);
00462     dWaitTime = dStartTime + 
00463         (uiTotalTickCount / sHdr.dSampleRate + dFragTime) - 
00464         GetTime();
00465 //g_print("hasas_op: wait time %f ms\n", dWaitTime * 1000.0);
00466     if (dWaitTime > 0.0)
00467         xmms_usleep((gint) (dWaitTime * 1e6));  // truncated because wait time is always a bit longer
00468     uiTotalTickCount += iTickCount;
00469     iPlayTime = iWriteTime;
00470 
00471     if (!bPause)
00472     {
00473         ReBuffer.Put(LocalAudioBuf, iSampleCount);
00474 
00475         do
00476         {
00477             MtxAudio.Wait();
00478             bData = ReBuffer.Get(AudioBuf, iFragmentSize);
00479             if (bData)
00480                 CndAudio.NotifyAll();
00481             MtxAudio.Release();
00482             if (bData)
00483                 xmms_usleep(1000);
00484         } while (bData);
00485     }
00486 }
00487 
00488 
00489 void clXMMSOut::CloseAudio ()
00490 {
00491     if (iMainThreadH < 0)
00492         return;
00493 
00494     Stop();
00495     XMMSOutThreads.Wait(iMainThreadH);
00496     iMainThreadH = -1;
00497 }
00498 
00499 
00500 void clXMMSOut::Flush (int iTime)
00501 {
00502     dStartTime = GetTime();
00503     iWriteTime = iTime;
00504     iPlayTime = iTime;
00505     uiTotalTickCount = 0;
00506 }
00507 
00508 
00509 void clXMMSOut::Pause (short iPaused)
00510 {
00511     bPause = (iPaused) ? true : false;
00512 }
00513 
00514 
00515 int clXMMSOut::BufferFree ()
00516 {
00517     //return ((iFragmentSize - ReBuffer.GetCount()) * sizeof(GDT));
00518     return iAudioBufSize;
00519 }
00520 
00521 
00522 int clXMMSOut::BufferPlaying ()
00523 {
00524     //return (bRun) ? true : false;
00525     return 0;
00526 }
00527 
00528 
00529 int clXMMSOut::OutputTime ()
00530 {
00531     return iPlayTime;
00532 }
00533 
00534 
00535 int clXMMSOut::WrittenTime ()
00536 {
00537     return iWriteTime;
00538 }
00539 
00540 
00541 void *clXMMSOut::MainThread (void *vpParam)
00542 {
00543     int iSockH;
00544 
00545     while (bRun)
00546     {
00547         iSockH = SServ.WaitForConnect(100);
00548         if (iSockH >= 0)
00549         {
00550 //g_print("hasas_op: incoming connection\n");
00551             XMMSOutThreads.Create(&clXMMSOut::ServeClientThread,
00552                 (void *) iSockH, true);
00553         }
00554     }
00555 
00556     return NULL;
00557 }
00558 
00559 
00560 void *clXMMSOut::ServeClientThread (void *vpParam)
00561 {
00562     stRawDataReq sReq;
00563     sigset_t sigsetThis;
00564     clAlloc LocalBuf;
00565     clSockOp SOp((int) vpParam);
00566 
00567 //g_print("hasas_op: in client thread\n");
00568     sigemptyset(&sigsetThis);
00569     sigaddset(&sigsetThis, SIGPIPE);
00570     sigaddset(&sigsetThis, SIGINT);
00571     sigaddset(&sigsetThis, SIGHUP);
00572     pthread_sigmask(SIG_BLOCK, &sigsetThis, NULL);
00573 
00574     if (SOp.WriteN(&sHdr, sizeof(sHdr)) < (int) sizeof(sHdr))
00575         return NULL;
00576     if (SOp.ReadN(&sReq, sizeof(sReq)) < (int) sizeof(sReq))
00577         return NULL;
00578     if (sReq.iChannel >= sHdr.iChannels)
00579         return NULL;
00580     while (bRun)
00581     {
00582         MtxAudio.Wait();
00583         CndAudio.Wait(MtxAudio.GetPtr());
00584         if (sReq.iChannel < 0)
00585         {
00586             LocalBuf.Size(iAudioBufSize);
00587             DSP.Copy((GDT *) LocalBuf, (GDT *) AudioBuf, iFragmentSize);
00588         }
00589         else
00590         {
00591             LocalBuf.Size(iAudioBufSize / sHdr.iChannels);
00592             CopyChannel(LocalBuf, AudioBuf, sReq.iChannel);
00593         }
00594         MtxAudio.Release();
00595 
00596         if (SOp.WriteN(LocalBuf, LocalBuf.GetSize()) < LocalBuf.GetSize())
00597             break;
00598     }
00599 //g_print("hasas_op: client disconnect or stop\n");
00600     
00601     return NULL;
00602 }
00603 
00604 
00605 void clXMMSOut::OnAboutButton (GtkButton *gbSender, gpointer gpData)
00606 {
00607     gtk_widget_destroy(gwMessageBox);
00608     g_free(cpMessageTxt);
00609 }
00610 
00611 
00612 void clXMMSOut::OnButtonClick (GtkButton *gbSender, gpointer gpData)
00613 {
00614     ConfigFile *cfCfgFile;
00615 
00616     if (GTK_BUTTON(gwBOk) == gbSender)
00617     {
00618         cfCfgFile = xmms_cfg_open_default_file();
00619         if (cfCfgFile == NULL)
00620             cfCfgFile = xmms_cfg_new();
00621         xmms_cfg_write_int(cfCfgFile, "hasas", "buffersize",
00622             atoi(gtk_entry_get_text(GTK_ENTRY(gwEBufSize))));
00623         xmms_cfg_write_string(cfCfgFile, "hasas", "localsocket",
00624             (gchar *) gtk_entry_get_text(GTK_ENTRY(gwELocalSocket)));
00625         xmms_cfg_write_default_file(cfCfgFile);
00626         xmms_cfg_free(cfCfgFile);
00627         
00628         SServ.Close();
00629         Init();
00630     }
00631     else if (GTK_BUTTON(gwBCancel) == gbSender)
00632     {
00633     }
00634     if (gwWinConfig)
00635     {
00636         gtk_widget_destroy(gwWinConfig);
00637         gwWinConfig = NULL;
00638     }
00639 }

Generated on Sun Oct 26 19:11:23 2003 for HASAS by doxygen 1.3.3