summaryrefslogtreecommitdiffstats
path: root/oggvorbis_artsplugin
diff options
context:
space:
mode:
Diffstat (limited to 'oggvorbis_artsplugin')
-rw-r--r--oggvorbis_artsplugin/Makefile.am23
-rw-r--r--oggvorbis_artsplugin/configure.in.in5
-rw-r--r--oggvorbis_artsplugin/oggPlayObject.mcopclass6
-rw-r--r--oggvorbis_artsplugin/oggPlayObject_impl.cpp313
-rw-r--r--oggvorbis_artsplugin/oggPlayObject_impl.h62
-rw-r--r--oggvorbis_artsplugin/oggarts.idl12
6 files changed, 421 insertions, 0 deletions
diff --git a/oggvorbis_artsplugin/Makefile.am b/oggvorbis_artsplugin/Makefile.am
new file mode 100644
index 00000000..aeec6205
--- /dev/null
+++ b/oggvorbis_artsplugin/Makefile.am
@@ -0,0 +1,23 @@
+# $Id$
+
+INCLUDES= -I$(kde_includes)/arts $(all_includes)
+
+noinst_HEADERS = oggPlayObject_impl.h
+
+lib_LTLIBRARIES = liboggarts.la
+liboggarts_la_COMPILE_FIRST = oggarts.h
+liboggarts_la_SOURCES = oggarts.cc oggPlayObject_impl.cpp
+liboggarts_la_LDFLAGS = $(all_libraries) -module -avoid-version -no-undefined
+liboggarts_la_LIBADD = -lkmedia2_idl -lsoundserver_idl -lartsflow -lvorbisfile -lvorbis -logg
+liboggarts_la_METASOURCES = AUTO
+
+oggarts.mcopclass: oggarts.h
+oggarts.mcoptype: oggarts.h
+oggarts.cc oggarts.h: $(srcdir)/oggarts.idl $(MCOPIDL)
+ $(MCOPIDL) -t -I$(kde_includes)/arts $(srcdir)/oggarts.idl
+
+mcoptypedir = $(libdir)/mcop
+mcoptype_DATA = oggarts.mcoptype oggarts.mcopclass
+
+mcopclassdir = $(libdir)/mcop/Arts
+mcopclass_DATA = oggPlayObject.mcopclass
diff --git a/oggvorbis_artsplugin/configure.in.in b/oggvorbis_artsplugin/configure.in.in
new file mode 100644
index 00000000..9efe4fe9
--- /dev/null
+++ b/oggvorbis_artsplugin/configure.in.in
@@ -0,0 +1,5 @@
+if test "$kde_mpeglib_compiles" = "yes"
+then
+DO_NOT_COMPILE="$DO_NOT_COMPILE oggvorbis_artsplugin"
+fi
+
diff --git a/oggvorbis_artsplugin/oggPlayObject.mcopclass b/oggvorbis_artsplugin/oggPlayObject.mcopclass
new file mode 100644
index 00000000..8128ea64
--- /dev/null
+++ b/oggvorbis_artsplugin/oggPlayObject.mcopclass
@@ -0,0 +1,6 @@
+Interface=Arts::oggPlayObject,Arts::PlayObject,Arts::SynthModule,Arts::Object
+Library=liboggarts.la
+Author=Alex Zepeda <zipzippy@sonic.net>
+Extension=ogg
+MimeType=audio/x-vorbis,application/ogg
+Language=C++
diff --git a/oggvorbis_artsplugin/oggPlayObject_impl.cpp b/oggvorbis_artsplugin/oggPlayObject_impl.cpp
new file mode 100644
index 00000000..80fd058e
--- /dev/null
+++ b/oggvorbis_artsplugin/oggPlayObject_impl.cpp
@@ -0,0 +1,313 @@
+/* $Id$ */
+
+#include <sys/types.h>
+#include <sys/ipc.h>
+#include <sys/sem.h>
+#include <sys/stat.h>
+#include <sys/shm.h>
+#include <sys/wait.h>
+
+#include <assert.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <math.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <math.h>
+#include <vorbis/codec.h>
+#include <vorbis/vorbisfile.h>
+
+/////////////////////////////////////////////////////////////
+// aRts interface
+
+#include <stdsynthmodule.h>
+#include "oggarts.h"
+#include <convert.h>
+#include <debug.h>
+
+
+#include "oggPlayObject_impl.h"
+
+using namespace Arts;
+
+oggPlayObject_impl::oggPlayObject_impl()
+{
+ struct shmid_ds bleh;
+ shm_id = shmget(IPC_PRIVATE, sizeof(*shm_buf), 0600);
+ shm_buf = (struct buf_t *)shmat(shm_id, 0, 0);
+
+ // mark it to be destroyed after the last detach
+ shmctl(shm_id, IPC_RMID, &bleh);
+
+ // sem0 has base
+ // sem1 remaining space
+ // sem2 seekTo
+ buflen_sem = semget(IPC_PRIVATE, 4, 0600);
+ child_pid = 0;
+}
+
+oggPlayObject_impl::~oggPlayObject_impl()
+{
+ halt();
+ union semun foo;
+ arts_debug("oggvorbis: removing IPC resources");
+ semctl(buflen_sem, 0, IPC_RMID, foo);
+}
+
+bool oggPlayObject_impl::loadMedia(const std::string &filename)
+{
+ halt(); // stop playing any previous stream
+ currentFile = filename;
+
+ //throw off a process to handle the decoding
+ //fill the buffers first...
+
+ short decode_buf[BACKBUFSIZ];
+
+ struct sembuf semoper;
+ semoper.sem_flg = 0;
+
+ buf_pos = 0;
+ // setup the initial semaphore joy
+
+ union semun foo;
+
+ foo.val = 0;
+ if (semctl(buflen_sem, 0, SETVAL, foo))
+ arts_debug("oggvorbis: couldn't clear queue %d, %s", errno, strerror(errno)); // no data in the queue
+
+ foo.val = 0;
+ // seekTo is -1 (ie, no seek)
+ if (semctl(buflen_sem, 2, SETVAL, foo))
+ arts_debug("oggvorbis: couldn't clear seekTo, %s",strerror(errno));
+
+ semctl(buflen_sem, 3, SETVAL, foo);
+ arts_debug("oggvorbis: seekTo is %d", semctl(buflen_sem, 2, GETVAL, foo));
+
+ // setup the starting free space in the buffer
+ foo.val = BACKBUFSIZ;
+ if (semctl(buflen_sem, 1, SETVAL, foo))
+ arts_debug("oggvorbis: couldn't mark buffer empty");
+
+ FILE *ogg_fp;
+ ogg_fp = fopen(filename.c_str(), "r");
+ int current_section=0;
+
+ ov_open(ogg_fp, &vf, NULL, 0);
+ if (!(child_pid = fork())) {
+ arts_debug("oggvorbis: child process");
+ do {
+ // get more data
+
+ int seekTo = semctl(buflen_sem, 2, GETVAL, foo);
+ if (seekTo) {
+ arts_debug("oggvorbis: seeking to %d", seekTo);
+ // we have a seek command
+ int ret = ov_time_seek(&vf, (double)seekTo-1);
+ arts_debug("oggvorbis: ov_time_seek returned: %d\n", ret);
+ foo.val=0;
+ semctl(buflen_sem, 2, SETVAL, foo); // we've done it
+ }
+ foo.val=(long)ov_time_tell(&vf);
+ if (foo.val == -1) foo.val=0;
+ semctl(buflen_sem, 3, SETVAL, foo);
+
+ int thisPass = ov_read(&vf, (char *)decode_buf, BACKBUFSIZ*sizeof(short), 0, 2, 1, &current_section);
+ if (thisPass == 0) {
+ // we're done, or we errored (in which case we're done)
+ break;
+ }
+ thisPass = (thisPass / 4);
+
+
+ semoper.sem_num = 1;
+ semoper.sem_op = -thisPass;
+ semop(buflen_sem, &semoper, 1);
+
+ // block until there's enough space to stick in this frame
+ int roomFor = semctl(buflen_sem, 1, GETVAL, foo);
+ if (roomFor > BACKBUFSIZ) {
+ arts_debug("oggvorbis: exit requested, bye!");
+ // this can never go above BACKBUFSIZ in normal operation,
+ // the consumer wants us to exit
+ break;
+ }
+
+ for (int i=0 ; i <thisPass ; ++i, buf_pos = ((buf_pos + 1) % BACKBUFSIZ)) {
+ shm_buf->left[buf_pos] = conv_16le_float(decode_buf[2*i]);
+ shm_buf->right[buf_pos] = conv_16le_float(decode_buf[2*i+1]);
+ }
+
+ //arts_debug("oggvorbis: enqueued them");
+ semoper.sem_num = 0;
+ semoper.sem_op = thisPass;
+ semop(buflen_sem, &semoper, 1); // mark the additional data now available
+
+ //arts_debug("oggvorbis: calculated %d more samples",shm_buf->back$
+ } while(1);
+
+ //signal completion
+ foo.val = 0;
+ // no more data available
+ semctl(buflen_sem, 0, SETVAL, foo);
+ // and no room either (ie, none coming)
+ semctl(buflen_sem, 1, SETVAL, foo);
+
+ arts_debug("oggvorbis: decoder process exiting");
+ exit(0);
+ }
+ return true;
+}
+
+std::string oggPlayObject_impl::description()
+{
+ return "ogg/vorbis artsplugin - this is my w00t!";
+}
+
+poTime oggPlayObject_impl::currentTime()
+{
+ poTime time;
+ union semun foo;
+ foo.val=0;
+ time.seconds = semctl(buflen_sem, 3, GETVAL, foo);
+ if (time.seconds == -1)
+ time.seconds = 0; // Eek infinite loop time.
+ time.ms=0;
+ //arts_debug("oggvorbis: time is now %d\n", time.seconds);
+ return time;
+}
+
+poTime oggPlayObject_impl::overallTime() {
+ poTime time;
+ time.seconds=(long)ov_time_total(&vf, -1);
+ time.ms=0;
+ return time;
+}
+
+poCapabilities oggPlayObject_impl::capabilities() {
+ return static_cast<poCapabilities>(capPause|capSeek);
+}
+
+std::string oggPlayObject_impl::mediaName() {
+ return currentFile;
+}
+
+poState oggPlayObject_impl::state() {
+ return mState;
+}
+
+void oggPlayObject_impl::play() {
+ arts_debug("oggvorbis: play");
+ mState = posPlaying;
+}
+
+void oggPlayObject_impl::halt()
+{
+ mState = posIdle;
+ union semun foo;
+
+ if (child_pid) {
+ arts_debug("oggvorbis: killing decoder process");
+ foo.val = 2*BACKBUFSIZ;
+ semctl(buflen_sem, 1, SETVAL, foo);
+ waitpid(child_pid, NULL, 0);
+ child_pid = 0;
+ }
+ // tell the producer to exit (would also result in us halting, if we weren't already)
+
+ // mainly this is to ensure that the decoder wakes up to notice
+}
+
+void oggPlayObject_impl::seek(const class poTime &t)
+{
+ union semun foo;
+
+ // this index is one-based so 0 can represent no seek
+ foo.val = static_cast<int>(t.seconds);
+ arts_debug("requesting seek to %d", foo.val);
+ semctl(buflen_sem, 2, SETVAL, foo); // we've done it
+}
+
+void oggPlayObject_impl::pause()
+{
+ mState = posPaused;
+}
+
+void oggPlayObject_impl::streamInit()
+{
+ arts_debug("oggvorbis: streamInit");
+}
+
+void oggPlayObject_impl::streamStart()
+{
+ arts_debug("ogg_vorbis: streamStart");
+}
+
+void oggPlayObject_impl::calculateBlock(unsigned long samples)
+{
+ int samplesAvailable = 0;
+
+ //arts_debug("oggvorbis: calculateBlock");
+
+ if (mState == posPlaying) {
+ //arts_debug("oggvorbis: calculateBlock, %d(%d) of %d samples in buffer",
+ //shm_buf->buflen - bufpos, shm_buf->backbuflen,samples);
+
+ struct sembuf bleh;
+
+ bleh.sem_num = 0;
+ bleh.sem_flg = IPC_NOWAIT;
+ //arts_debug("oggvorbis: %d samples wanted", samplesAvailable);
+ bleh.sem_op = -samples; // does the buffer have sufficient samples?
+ if (semop(buflen_sem, &bleh, 1) == -1) {
+ if (errno == EAGAIN) {
+ union semun foo;
+ arts_debug("oggvorbis: buffer underrun");
+ samplesAvailable = semctl(buflen_sem, 0, GETVAL, foo);
+ // no samples AND no room is the decoder's way of signalling completion
+ if (semctl(buflen_sem, 1, GETVAL, foo) == 0) {
+ halt();
+ samplesAvailable = 0;
+ }
+ } else {
+ // something awful has happened
+ halt();
+ samplesAvailable = 0;
+ }
+ } else {
+ samplesAvailable = samples; // number of samples we pushed from buffers
+ // used to calculate the number we should zero out for an underrun
+ }
+ bleh.sem_flg = 0; // back to normal now
+
+ //arts_debug("oggvorbis: %d samples available",samplesAvailable);
+ for (int i = 0; i < samplesAvailable;
+ ++i, buf_pos = ((buf_pos + 1) % BACKBUFSIZ)) {
+
+ left[i] = shm_buf->left[buf_pos];
+ right[i] = shm_buf->right[buf_pos];
+ }
+
+ bleh.sem_num = 1;
+ bleh.sem_op = samplesAvailable;
+ semop(buflen_sem, &bleh, 1); // mark the now-free space
+ }
+ // zero out any samples we didn't have enough to complete
+ while(static_cast<unsigned long>(samplesAvailable) < samples) {
+ left[samplesAvailable] = 0.0;
+ right[samplesAvailable] = 0.0;
+ samplesAvailable++;
+ }
+}
+
+void oggPlayObject_impl::streamEnd()
+{
+ arts_debug("oggvorbis: streamEnd");
+}
+
+REGISTER_IMPLEMENTATION(oggPlayObject_impl);
diff --git a/oggvorbis_artsplugin/oggPlayObject_impl.h b/oggvorbis_artsplugin/oggPlayObject_impl.h
new file mode 100644
index 00000000..2f1b0c0c
--- /dev/null
+++ b/oggvorbis_artsplugin/oggPlayObject_impl.h
@@ -0,0 +1,62 @@
+#ifndef OGGPLAYER_IMPL_H
+#define OGGPLAYER_IMPL_H "$Id$"
+
+#if defined(__GNU_LIBRARY__) && !defined(_SEM_SEMUN_UNDEFINED) || defined(__DragonFly__)
+/* union semun is defined by including <sys/sem.h> */
+#else
+/* according to X/OPEN we have to define it ourselves */
+union semun {
+ int val; /* value for SETVAL */
+ struct semid_ds *buf; /* buffer for IPC_STAT, IPC_SET */
+ unsigned short int *array; /* array for GETALL, SETALL */
+ struct seminfo *__buf; /* buffer for IPC_INFO */
+};
+#endif
+
+int buf_pos;
+
+namespace Arts {
+
+class oggPlayObject_impl
+ : public oggPlayObject_skel, public StdSynthModule
+{
+ public:
+ oggPlayObject_impl();
+ ~oggPlayObject_impl();
+ bool loadMedia(const std::string &filename);
+ std::string description();
+ poTime currentTime();
+ poTime overallTime();
+ poCapabilities capabilities();
+ std::string mediaName();
+ poState state();
+ void play();
+ void halt();
+ void seek(const class poTime &t);
+ void pause();
+ void streamInit();
+ void streamStart();
+ void calculateBlock(unsigned long samples);
+ void streamEnd();
+
+ protected:
+
+ static const int BACKBUFSIZ=4096;
+ OggVorbis_File vf;
+ std::string currentFile;
+
+ inline float conv_16le_float(short x)
+ { return static_cast<float>(x) / 32768.0; }
+
+ poState mState;
+ struct buf_t{
+ float left[BACKBUFSIZ];
+ float right[BACKBUFSIZ];
+ } *shm_buf;
+ int shm_id, child_pid;
+ int buflen_sem;
+};
+
+};
+
+#endif
diff --git a/oggvorbis_artsplugin/oggarts.idl b/oggvorbis_artsplugin/oggarts.idl
new file mode 100644
index 00000000..c56deac9
--- /dev/null
+++ b/oggvorbis_artsplugin/oggarts.idl
@@ -0,0 +1,12 @@
+#include <kmedia2.idl>
+#include <soundserver.idl>
+
+module Arts
+{
+
+interface oggPlayObject : PlayObject, SynthModule
+{
+ out audio stream left,right;
+};
+
+};