00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
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
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
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
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
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)
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
00408 Convert8s8u(vpData, iSize / sizeof(char));
00409 case FMT_U8:
00410
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
00419
00420
00421 # endif
00422 case FMT_U16_BE:
00423 # if (G_BYTE_ORDER == G_LITTLE_ENDIAN)
00424
00425
00426
00427 # endif
00428 case FMT_U16_NE:
00429
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
00439
00440
00441 # endif
00442 case FMT_S16_BE:
00443 # if (G_BYTE_ORDER == G_LITTLE_ENDIAN)
00444
00445
00446
00447 # endif
00448 case FMT_S16_NE:
00449
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
00462 dWaitTime = dStartTime +
00463 (uiTotalTickCount / sHdr.dSampleRate + dFragTime) -
00464 GetTime();
00465
00466 if (dWaitTime > 0.0)
00467 xmms_usleep((gint) (dWaitTime * 1e6));
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
00518 return iAudioBufSize;
00519 }
00520
00521
00522 int clXMMSOut::BufferPlaying ()
00523 {
00524
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
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
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
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 }