From bdc29e6a6e2b4dd1d08bda14c8756b2435a2baea Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Revol?= Date: Tue, 20 Jun 2006 22:17:12 +0000 Subject: [PATCH] Add my ESound Daemon Sink media node source code. I need it on an other box anyway... Currently uses hardcoded server IP. Jamfile likely doesn't work (needs net libs); and requires ZETA due to TextParameter (for server IP but unused yet; I think Haiku should have it too) will need some ifdefs Use the provided makefile for now. git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@17886 a95241bf-73f2-0310-859d-f6bbb57e9c96 --- .../media-add-ons/esound_sink/ESDEndpoint.cpp | 316 ++++ .../media-add-ons/esound_sink/ESDEndpoint.h | 84 + .../esound_sink/ESDSinkAddOn.cpp | 306 ++++ .../media-add-ons/esound_sink/ESDSinkAddOn.h | 87 + .../media-add-ons/esound_sink/ESDSinkNode.cpp | 1449 +++++++++++++++++ .../media-add-ons/esound_sink/ESDSinkNode.h | 364 +++++ .../esound_sink/EsounD-protocol.txt | 279 ++++ .../media/media-add-ons/esound_sink/Jamfile | 20 + .../media/media-add-ons/esound_sink/compat.h | 26 + .../media/media-add-ons/esound_sink/debug.h | 34 + .../media-add-ons/esound_sink/esdproto.h | 131 ++ .../media/media-add-ons/esound_sink/makefile | 128 ++ 12 files changed, 3224 insertions(+) create mode 100644 src/add-ons/media/media-add-ons/esound_sink/ESDEndpoint.cpp create mode 100644 src/add-ons/media/media-add-ons/esound_sink/ESDEndpoint.h create mode 100644 src/add-ons/media/media-add-ons/esound_sink/ESDSinkAddOn.cpp create mode 100644 src/add-ons/media/media-add-ons/esound_sink/ESDSinkAddOn.h create mode 100644 src/add-ons/media/media-add-ons/esound_sink/ESDSinkNode.cpp create mode 100644 src/add-ons/media/media-add-ons/esound_sink/ESDSinkNode.h create mode 100644 src/add-ons/media/media-add-ons/esound_sink/EsounD-protocol.txt create mode 100644 src/add-ons/media/media-add-ons/esound_sink/Jamfile create mode 100644 src/add-ons/media/media-add-ons/esound_sink/compat.h create mode 100644 src/add-ons/media/media-add-ons/esound_sink/debug.h create mode 100644 src/add-ons/media/media-add-ons/esound_sink/esdproto.h create mode 100644 src/add-ons/media/media-add-ons/esound_sink/makefile diff --git a/src/add-ons/media/media-add-ons/esound_sink/ESDEndpoint.cpp b/src/add-ons/media/media-add-ons/esound_sink/ESDEndpoint.cpp new file mode 100644 index 0000000000..a4ca123a92 --- /dev/null +++ b/src/add-ons/media/media-add-ons/esound_sink/ESDEndpoint.cpp @@ -0,0 +1,316 @@ +/* + * ESounD media addon for BeOS + * + * Copyright (c) 2006 François Revol (revol@free.fr) + * + * Based on Multi Audio addon for Haiku, + * Copyright (c) 2002, 2003 Jerome Duval (jerome.duval@free.fr) + * + * All rights reserved. + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * - Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * - Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ +#define _ZETA_TS_FIND_DIR_ 1 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "compat.h" +//#undef DEBUG +//#define DEBUG 4 +#include "debug.h" +#include +#include "ESDEndpoint.h" + +ESDEndpoint::ESDEndpoint() + : BDataIO() + , fHost(NULL) + , fPort(ESD_PORT) + , fSocket(-1) + , fDefaultCommand(ESD_CMD_STREAM_PLAY) + , fDefaultCommandSent(false) + , fDefaultFormat(ESD_BITS8 | ESD_MONO) + , fDefaultRate(ESD_DEFAULT_RATE) + , fLatency(0LL) +{ + CALLED(); +} + +ESDEndpoint::~ESDEndpoint() +{ + CALLED(); + if (fSocket > -1) + closesocket(fSocket); + fSocket = -1; +} + +status_t ESDEndpoint::SendAuthKey() +{ + CALLED(); + BPath kfPath; + status_t err; + off_t size; + char key[ESD_MAX_KEY]; + err = find_directory(B_USER_SETTINGS_DIRECTORY, &kfPath); + kfPath.Append("esd_auth"); + BFile keyFile(kfPath.Path(), B_READ_WRITE|B_CREATE_FILE); + err = keyFile.GetSize(&size); + if (err < 0) + return err; + if (size < ESD_MAX_KEY) { + keyFile.Seek(0LL, SEEK_SET); + srand(time(NULL)); + for (int i = 0; i < ESD_MAX_KEY; i++) + key[i] = (char)(rand() % 256); + err = keyFile.Write(key, ESD_MAX_KEY); + if (err < 0) + return err; + if (err < ESD_MAX_KEY) + return EIO; + } + err = keyFile.Read(key, ESD_MAX_KEY); + if (err < 0) + return err; + if (err < ESD_MAX_KEY) + return EIO; + memcpy(fAuthKey, key, sizeof(esd_key_t)); + return write(fSocket, fAuthKey, ESD_MAX_KEY); +} + +status_t ESDEndpoint::Connect(const char *host, uint16 port) +{ + status_t err; + CALLED(); + fHost = host; + fPort = port; + + struct hostent *he; + struct sockaddr_in sin; + he = gethostbyname(host); + PRINT(("gethostbyname(%s) = %p\n", host, he)); + if (!he) + return ENOENT; + memcpy((struct in_addr *)&sin.sin_addr, he->h_addr, sizeof(struct in_addr)); + + fSocket = socket(AF_INET, SOCK_STREAM, 0); + if (fSocket < 0) + return errno; + sin.sin_family = AF_INET; + sin.sin_port = htons( port ); + + err = connect(fSocket, (struct sockaddr *) &sin, sizeof(sin)); + PRINT(("connect: %s\n", strerror(err))); + if (err < 0) + return errno; + +/* uint32 cmd = ESD_CMD_CONNECT; + err = write(fSocket, &cmd, sizeof(cmd)); + if (err < 0) + return errno; + if (err < sizeof(cmd)) + return EIO; +*/ + err = SendAuthKey(); + if (err < 0) + return errno; + + bigtime_t ping = system_time(); + + uint32 endian = ESD_ENDIAN_TAG; + err = write(fSocket, &endian, sizeof(endian)); + if (err < 0) + return errno; + if (err < sizeof(endian)) + return EIO; + uint32 ok; + + read(fSocket, &ok, sizeof(uint32)); + + ping = system_time() - ping; + fLatency = ping; + + int flag = 1; + setsockopt(fSocket, IPPROTO_TCP, TCP_NODELAY, &flag, sizeof(flag)); + +// read(fSocket, &ok, sizeof(uint32)); +// connect +// auth +// ask server latency +// calc network latency (time (send+recv) / 2) ? +// get default format + + return B_OK; +} + +status_t ESDEndpoint::Disconnect() +{ + CALLED(); + if (fSocket > -1) + closesocket(fSocket); + fSocket = -1; + return B_OK; +} + +status_t ESDEndpoint::SetCommand(esd_command_t cmd) +{ + CALLED(); + if (fDefaultCommandSent) + return EALREADY; + fDefaultCommand = cmd; + return B_OK; +} + +status_t ESDEndpoint::SetFormat(int bits, int channels, float rate) +{ + esd_format_t fmt = 0; + CALLED(); + if (fDefaultCommandSent) + return EALREADY; + PRINT(("SetFormat(%d,%d,%d)\n", bits, channels, rate)); + switch (bits) { + case 8: + fmt |= ESD_BITS8; + break; + case 16: + fmt |= ESD_BITS16; + break; + default: + return EINVAL; + } + switch (channels) { + case 1: + fmt |= ESD_MONO; + break; + case 2: + fmt |= ESD_STEREO; + break; + default: + return EINVAL; + } + fmt |= ESD_STREAM | ESD_FUNC_PLAY; + PRINT(("SetFormat: %08lx\n", fmt)); + fDefaultFormat = fmt; + fDefaultRate = rate; + return B_OK; +} + +status_t ESDEndpoint::GetServerInfo() +{ + CALLED(); + struct serverinfo { + uint32 ver; + uint32 rate; + uint32 fmt; + } si; + status_t err; + err = SendCommand(ESD_CMD_SERVER_INFO, (const uint8 *)&si, 0, (uint8 *)&si, sizeof(si)); + if (err < 0) + return err; + PRINT(("err %d, version: %lu, rate: %lu, fmt: %lu\n", err, si.ver, si.rate, si.fmt)); + return B_OK; +} + +bool ESDEndpoint::CanSend() +{ + CALLED(); + return fDefaultCommandSent; +} + +ssize_t ESDEndpoint::Read(void *buffer, size_t size) +{ + CALLED(); + return EINVAL; +} + +ssize_t ESDEndpoint::Write(const void *buffer, size_t size) +{ + status_t err = B_OK; + CALLED(); + if (!fDefaultCommandSent) + err = SendDefaultCommand(); + if (err < B_OK) + return err; + //PRINT(("write(fSocket, buffer, %d)\n", size)); + //fprintf(stderr, "ESDEndpoint::Write(, %d) %s\n", size, (size%2)?"ODD BUFFER SIZE":""); + if (fDefaultFormat & ESD_BITS16) { + size /= 2; + size *= 2; + } + err = write(fSocket, buffer, size); + if (err != size) { + fprintf(stderr, "ESDEndpoint::Write: sent only %d of %d!\n", err, size); + } + //PRINT(("write(fSocket, buffer, %d): %s\n", size, strerror(err))); + if (err < B_OK) + return errno; + return err; +} + +status_t ESDEndpoint::SendCommand(esd_command_t cmd, const uint8 *obuf, size_t olen, uint8 *ibuf, size_t ilen) +{ + status_t err; + CALLED(); + err = send(fSocket, &cmd, sizeof(cmd), 0); + if (err < B_OK) + return errno; + if (obuf && olen) { + err = send(fSocket, obuf, olen, 0); + if (err < B_OK) + return errno; + } + err = B_OK; + if (ibuf && ilen) { + err = recv(fSocket, ibuf, ilen, 0); + if (err < B_OK) + return errno; + /* return received len */ + } + return err; +} + +status_t ESDEndpoint::SendDefaultCommand() +{ + status_t err; + struct { + esd_format_t format; + esd_rate_t rate; + char name[ESD_MAX_NAME]; + } c; + CALLED(); + if (fDefaultCommandSent) + return EALREADY; + c.format = fDefaultFormat; + c.rate = fDefaultRate; + strcpy(c.name, "BeOS/Haiku/ZETA Media Kit output"); + err = SendCommand(fDefaultCommand, (uint8 *)&c, sizeof(c), NULL, 0); + if (err < B_OK) + return err; + PRINT(("SendCommand: %s\n", strerror(err))); + fDefaultCommandSent = true; + return B_OK; +} + diff --git a/src/add-ons/media/media-add-ons/esound_sink/ESDEndpoint.h b/src/add-ons/media/media-add-ons/esound_sink/ESDEndpoint.h new file mode 100644 index 0000000000..c199f30827 --- /dev/null +++ b/src/add-ons/media/media-add-ons/esound_sink/ESDEndpoint.h @@ -0,0 +1,84 @@ +/* + * ESounD media addon for BeOS + * + * Copyright (c) 2006 François Revol (revol@free.fr) + * + * Based on Multi Audio addon for Haiku, + * Copyright (c) 2002, 2003 Jerome Duval (jerome.duval@free.fr) + * + * All rights reserved. + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * - Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * - Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ +#ifndef ESDENDPOINT_H +#define ESDENDPOINT_H + +#include "esdproto.h" +#include +#include + +//#define ESD_FMT 8 +#define ESD_FMT 16 + +class ESDEndpoint : public BDataIO { +public: + ESDEndpoint(); + ~ESDEndpoint(); + + /* */ +status_t SendAuthKey(); +status_t Connect(const char *host, uint16 port=ESD_PORT); +status_t Disconnect(); + + /* set the default command and format for BDataIO interface */ +status_t SetCommand(esd_command_t cmd=ESD_CMD_STREAM_PLAY); +status_t SetFormat(int bits, int channels, float rate=ESD_DEFAULT_RATE); + +status_t GetServerInfo(); + + /* */ + +bigtime_t GetLatency() const { return fLatency; }; + +bool CanSend(); + + /* BDataIO */ + +virtual ssize_t Read(void *buffer, size_t size); +virtual ssize_t Write(const void *buffer, size_t size); + + status_t SendCommand(esd_command_t cmd, const uint8 *obuf, size_t olen, uint8 *ibuf, size_t ilen); + status_t SendDefaultCommand(); +private: + + BString fHost; + uint16 fPort; + int fSocket; + esd_key_t fAuthKey; + esd_command_t fDefaultCommand; + bool fDefaultCommandSent; + esd_format_t fDefaultFormat; + esd_rate_t fDefaultRate; + bigtime_t fLatency; +}; + + +#endif /* ESDENDPOINT_H */ diff --git a/src/add-ons/media/media-add-ons/esound_sink/ESDSinkAddOn.cpp b/src/add-ons/media/media-add-ons/esound_sink/ESDSinkAddOn.cpp new file mode 100644 index 0000000000..5ebf76c05c --- /dev/null +++ b/src/add-ons/media/media-add-ons/esound_sink/ESDSinkAddOn.cpp @@ -0,0 +1,306 @@ +/* + * ESounD media addon for BeOS + * + * Copyright (c) 2006 François Revol (revol@free.fr) + * + * Based on Multi Audio addon for Haiku, + * Copyright (c) 2002, 2003 Jerome Duval (jerome.duval@free.fr) + * + * All rights reserved. + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * - Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * - Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ +#define _ZETA_TS_FIND_DIR_ 1 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "ESDSinkNode.h" +#include "ESDSinkAddOn.h" +#include "ESDEndpoint.h" + +#include +#include +#include +//#undef DEBUG +//#define DEBUG 4 +#include "debug.h" +#include + +//#define MULTI_SAVE + +// instantiation function +extern "C" _EXPORT BMediaAddOn * make_media_addon(image_id image) { + CALLED(); + return new ESDSinkAddOn(image); +} + +// -------------------------------------------------------- // +// ctor/dtor +// -------------------------------------------------------- // + +ESDSinkAddOn::~ESDSinkAddOn() +{ + CALLED(); + + void *device = NULL; + for ( int32 i = 0; (device = fDevices.ItemAt(i)); i++ ) + delete (ESDEndpoint *)device; + + SaveSettings(); +} + +ESDSinkAddOn::ESDSinkAddOn(image_id image) : + BMediaAddOn(image), + fDevices() +{ + CALLED(); + fInitCheckStatus = B_NO_INIT; + + LoadSettings(); + + if(SetupDefaultSinks()!=B_OK) + return; + + fInitCheckStatus = B_OK; +} + +// -------------------------------------------------------- // +// BMediaAddOn impl +// -------------------------------------------------------- // + +status_t ESDSinkAddOn::InitCheck( + const char ** out_failure_text) +{ + CALLED(); + return B_OK; +} + +int32 ESDSinkAddOn::CountFlavors() +{ + CALLED(); + //return fDevices.CountItems(); + return 1; +} + +status_t ESDSinkAddOn::GetFlavorAt( + int32 n, + const flavor_info ** out_info) +{ + CALLED(); + if (out_info == 0) { + fprintf(stderr,"<- B_BAD_VALUE\n"); + return B_BAD_VALUE; // we refuse to crash because you were stupid + } + //if (n < 0 || n > fDevices.CountItems() - 1) { + if (n < 0 || n > 1) { + fprintf(stderr,"<- B_BAD_INDEX\n"); + return B_BAD_INDEX; + } + + ESDEndpoint *device = (ESDEndpoint *) fDevices.ItemAt(n); + + flavor_info * infos = new flavor_info[1]; + ESDSinkNode::GetFlavor(&infos[0], n); +// infos[0].name = device->MD.friendly_name; + infos[0].name = "ESounD Out"; + (*out_info) = infos; + return B_OK; +} + +BMediaNode * ESDSinkAddOn::InstantiateNodeFor( + const flavor_info * info, + BMessage * config, + status_t * out_error) +{ + CALLED(); + if (out_error == 0) { + fprintf(stderr,"<- NULL\n"); + return 0; // we refuse to crash because you were stupid + } + + BMessage defaults; + if (!config) + config = &defaults; + +#ifdef MULTI_SAVE + if(fSettings.FindMessage(device->MD.friendly_name, config)==B_OK) { + fSettings.RemoveData(device->MD.friendly_name); + } +#endif + + + ESDSinkNode * node + = new ESDSinkNode(this, + "ESounD Sink", + config); + if (node == 0) { + *out_error = B_NO_MEMORY; + fprintf(stderr,"<- B_NO_MEMORY\n"); + } else { + *out_error = node->InitCheck(); + } + return node; +} + +status_t +ESDSinkAddOn::GetConfigurationFor(BMediaNode * your_node, BMessage * into_message) +{ + CALLED(); +#ifdef MULTI_SAVE + if (into_message == 0) { + into_message = new BMessage(); + ESDSinkNode * node = dynamic_cast(your_node); + if (node == 0) { + fprintf(stderr,"<- B_BAD_TYPE\n"); + return B_BAD_TYPE; + } + if(node->GetConfigurationFor(into_message)==B_OK) { + fSettings.AddMessage(your_node->Name(), into_message); + } + return B_OK; + } +#endif + // currently never called by the media kit. Seems it is not implemented. + if (into_message == 0) { + fprintf(stderr,"<- B_BAD_VALUE\n"); + return B_BAD_VALUE; // we refuse to crash because you were stupid + } + ESDSinkNode * node = dynamic_cast(your_node); + if (node == 0) { + fprintf(stderr,"<- B_BAD_TYPE\n"); + return B_BAD_TYPE; + } + return node->GetConfigurationFor(into_message); +} + +#if 0 +bool ESDSinkAddOn::WantsAutoStart() +{ + CALLED(); + return true;//false; +} + +status_t ESDSinkAddOn::AutoStart( + int in_count, + BMediaNode ** out_node, + int32 * out_internal_id, + bool * out_has_more) +{ + CALLED(); + const flavor_info *fi; + status_t err; + + // XXX: LEAK! + PRINT(("AutoStart: in_count=%d\n", in_count)); +// if (in_count < 1) +// return EINVAL; + *out_internal_id = 0; + *out_has_more = false; + err = GetFlavorAt(0, (const flavor_info **)&fi); + if (err < 0) + return err; + *out_node = InstantiateNodeFor((const flavor_info *)fi, NULL, &err); + delete fi; + if (err < 0) + return err; + return B_OK+1; +} +#endif + +status_t +ESDSinkAddOn::SetupDefaultSinks() +{ + CALLED(); +#if 0 + BDirectory root; + if(rootEntry!=NULL) + root.SetTo(rootEntry); + else if(rootPath!=NULL) { + root.SetTo(rootPath); + } else { + PRINT(("Error in ESDSinkAddOn::RecursiveScan null params\n")); + return B_ERROR; + } + + BEntry entry; + + while(root.GetNextEntry(&entry) > B_ERROR) { + + if(entry.IsDirectory()) { + RecursiveScan(rootPath, &entry); + } else { + BPath path; + entry.GetPath(&path); + ESDEndpoint *device = new ESDEndpoint(path.Path() + strlen(rootPath), path.Path()); + if (device) { + if (device->InitCheck() == B_OK) + fDevices.AddItem(device); + else + delete device; + } + } + } + +#endif + return B_OK; +} + + +void +ESDSinkAddOn::SaveSettings(void) +{ + CALLED(); + BPath path; + if(find_directory(B_USER_SETTINGS_DIRECTORY, &path) == B_OK) { + path.Append(SETTINGS_FILE); + BFile file(path.Path(),B_READ_WRITE|B_CREATE_FILE|B_ERASE_FILE); + if(file.InitCheck()==B_OK) + fSettings.Flatten(&file); + } +} + + +void +ESDSinkAddOn::LoadSettings(void) +{ + CALLED(); + fSettings.MakeEmpty(); + + BPath path; + if(find_directory(B_USER_SETTINGS_DIRECTORY, &path) == B_OK) { + path.Append(SETTINGS_FILE); + BFile file(path.Path(),B_READ_ONLY); + if((file.InitCheck()==B_OK)&&(fSettings.Unflatten(&file)==B_OK)) + { + //fSettings.PrintToStream(); + } else { + PRINT(("Error unflattening settings file %s\n",path.Path())); + } + } +} diff --git a/src/add-ons/media/media-add-ons/esound_sink/ESDSinkAddOn.h b/src/add-ons/media/media-add-ons/esound_sink/ESDSinkAddOn.h new file mode 100644 index 0000000000..d2ac025f35 --- /dev/null +++ b/src/add-ons/media/media-add-ons/esound_sink/ESDSinkAddOn.h @@ -0,0 +1,87 @@ +/* + * ESounD media addon for BeOS + * + * Copyright (c) 2006 François Revol (revol@free.fr) + * + * Based on Multi Audio addon for Haiku, + * Copyright (c) 2002, 2003 Jerome Duval (jerome.duval@free.fr) + * + * All rights reserved. + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * - Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * - Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ +#ifndef _ESDSINK_ADDON_H +#define _ESDSINK_ADDON_H + +#include +#include + +#define SETTINGS_FILE "Media/esd_sink_settings" + +class ESDSinkAddOn : + public BMediaAddOn +{ +public: + virtual ~ESDSinkAddOn(void); + explicit ESDSinkAddOn(image_id image); + +/**************************/ +/* begin from BMediaAddOn */ +public: +virtual status_t InitCheck( + const char ** out_failure_text); +virtual int32 CountFlavors(void); +virtual status_t GetFlavorAt( + int32 n, + const flavor_info ** out_info); +virtual BMediaNode * InstantiateNodeFor( + const flavor_info * info, + BMessage * config, + status_t * out_error); +virtual status_t GetConfigurationFor( + BMediaNode * your_node, + BMessage * into_message); +/* +virtual bool WantsAutoStart(void); +virtual status_t AutoStart( + int in_count, + BMediaNode ** out_node, + int32 * out_internal_id, + bool * out_has_more); +*/ + +/* end from BMediaAddOn */ +/************************/ + +private: + status_t SetupDefaultSinks(); + void SaveSettings(); + void LoadSettings(); + + status_t fInitCheckStatus; + BList fDevices; + + BMessage fSettings; // settings loaded from settings directory +}; + +extern "C" _EXPORT BMediaAddOn *make_media_addon( image_id you ); + +#endif /* _ESDSINK_ADDON_H */ diff --git a/src/add-ons/media/media-add-ons/esound_sink/ESDSinkNode.cpp b/src/add-ons/media/media-add-ons/esound_sink/ESDSinkNode.cpp new file mode 100644 index 0000000000..44a5b82a0e --- /dev/null +++ b/src/add-ons/media/media-add-ons/esound_sink/ESDSinkNode.cpp @@ -0,0 +1,1449 @@ +/* + * ESounD media addon for BeOS + * + * Copyright (c) 2006 François Revol (revol@free.fr) + * + * Based on Multi Audio addon for Haiku, + * Copyright (c) 2002, 2003 Jerome Duval (jerome.duval@free.fr) + * + * All rights reserved. + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * - Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * - Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "ESDSinkNode.h" +#include "ESDEndpoint.h" +#ifdef DEBUG + #define PRINTING +#endif +#include "debug.h" +#include + +#include +#include + +const char * multi_string[] = +{ + "NAME IS ATTACHED", + "Output", "Input", "Setup", "Tone Control", "Extended Setup", "Enhanced Setup", "Master", + "Beep", "Phone", "Mic", "Line", "CD", "Video", "Aux", "Wave", "Gain", "Level", "Volume", + "Mute", "Enable", "Stereo Mix", "Mono Mix", "Output Stereo Mix", "Output Mono Mix", "Output Bass", + "Output Treble", "Output 3D Center", "Output 3D Depth" +}; + + +// -------------------------------------------------------- // +// ctor/dtor +// -------------------------------------------------------- // + +ESDSinkNode::~ESDSinkNode(void) +{ + CALLED(); + fAddOn->GetConfigurationFor(this, NULL); + + BMediaEventLooper::Quit(); + + fWeb = NULL; + delete fDevice; +} + +ESDSinkNode::ESDSinkNode(BMediaAddOn *addon, char* name, BMessage * config) + : BMediaNode(name), + BBufferConsumer(B_MEDIA_RAW_AUDIO), +#if ENABLE_INPUT + BBufferProducer(B_MEDIA_RAW_AUDIO), +#endif +#ifdef ENABLE_TS + BTimeSource(), +#endif + BMediaEventLooper(), + fThread(-1), + fDevice(NULL), + fTimeSourceStarted(false), + fWeb(NULL), + fConfig(*config) +{ + CALLED(); + fInitCheckStatus = B_NO_INIT; + + fAddOn = addon; + fId = 0; + + AddNodeKind( B_PHYSICAL_OUTPUT ); +#if ENABLE_INPUT + AddNodeKind( B_PHYSICAL_INPUT ); +#endif + + // initialize our preferred format object + memset(&fPreferredFormat, 0, sizeof(fPreferredFormat)); // set everything to wildcard first + fPreferredFormat.type = B_MEDIA_RAW_AUDIO; +#if ESD_FMT == 8 + fPreferredFormat.u.raw_audio.format = media_raw_audio_format::B_AUDIO_UCHAR; +#else + fPreferredFormat.u.raw_audio.format = media_raw_audio_format::B_AUDIO_SHORT; +#endif + fPreferredFormat.u.raw_audio.valid_bits = 0; + fPreferredFormat.u.raw_audio.channel_count = 2; + fPreferredFormat.u.raw_audio.frame_rate = ESD_DEFAULT_RATE; + fPreferredFormat.u.raw_audio.byte_order = B_MEDIA_HOST_ENDIAN; + + // we'll use the consumer's preferred buffer size, if any + fPreferredFormat.u.raw_audio.buffer_size = ESD_MAX_BUF / 4 +/* * (fPreferredFormat.u.raw_audio.format & media_raw_audio_format::B_AUDIO_SIZE_MASK) + * fPreferredFormat.u.raw_audio.channel_count*/; + + if(config) { + //PRINT_OBJECT(*config); + config->FindString("hostname", &fHostname); + } + if (fHostname.Length() < 1) + fHostname = "192.168.0.253"; + + fDevice = new ESDEndpoint(); + if (fDevice) { + if (fDevice->Connect(fHostname.String()) >= 0) { + fDevice->SetCommand(); + fDevice->SetFormat(ESD_FMT, 2); + //fDevice->GetServerInfo(); + fInitCheckStatus = fDevice->SendDefaultCommand(); + } + } +} + +status_t ESDSinkNode::InitCheck(void) const +{ + CALLED(); + return fInitCheckStatus; +} + + +// -------------------------------------------------------- // +// implementation of BMediaNode +// -------------------------------------------------------- // + +BMediaAddOn * ESDSinkNode::AddOn( + int32 * internal_id) const +{ + CALLED(); + // BeBook says this only gets called if we were in an add-on. + if (fAddOn != 0) { + // If we get a null pointer then we just won't write. + if (internal_id != 0) { + *internal_id = fId; + } + } + return fAddOn; +} + +void ESDSinkNode::Preroll(void) +{ + CALLED(); + // XXX:Performance opportunity + BMediaNode::Preroll(); +} + +status_t ESDSinkNode::HandleMessage( + int32 message, + const void * data, + size_t size) +{ + CALLED(); + return B_ERROR; +} + +void ESDSinkNode::NodeRegistered(void) +{ + CALLED(); + + if (fInitCheckStatus != B_OK) { + ReportError(B_NODE_IN_DISTRESS); + return; + } + + SetPriority(B_REAL_TIME_PRIORITY); + + Run(); + +// media_input *input = new media_input; + + fInput.format = fPreferredFormat; + fInput.destination.port = ControlPort(); + fInput.destination.id = 0; + fInput.node = Node(); + sprintf(fInput.name, "output %ld", fInput.destination.id); + + fOutput.format = fPreferredFormat; + fOutput.destination = media_destination::null; + fOutput.source.port = ControlPort(); + fOutput.source.id = 0; + fOutput.node = Node(); + sprintf(fOutput.name, "input %ld", fOutput.source.id); + + // Set up our parameter web + fWeb = MakeParameterWeb(); + SetParameterWeb(fWeb); + + /* apply configuration */ +#ifdef PRINTING + bigtime_t start = system_time(); +#endif + + int32 index = 0; + int32 parameterID = 0; + const void *data; + ssize_t size; + while(fConfig.FindInt32("parameterID", index, ¶meterID) == B_OK) { + if(fConfig.FindData("parameterData", B_RAW_TYPE, index, &data, &size) == B_OK) + SetParameterValue(parameterID, TimeSource()->Now(), data, size); + index++; + } + +#ifdef PRINTING + PRINT(("apply configuration in : %ld\n", system_time() - start)); +#endif +} + +status_t ESDSinkNode::RequestCompleted(const media_request_info &info) +{ + CALLED(); + return B_OK; +} + +void ESDSinkNode::SetTimeSource(BTimeSource *timeSource) +{ + CALLED(); +} + +// -------------------------------------------------------- // +// implemention of BBufferConsumer +// -------------------------------------------------------- // + +// Check to make sure the format is okay, then remove +// any wildcards corresponding to our requirements. +status_t ESDSinkNode::AcceptFormat( + const media_destination & dest, + media_format * format) +{ + CALLED(); + + if(fInput.destination != dest) { + fprintf(stderr,"<- B_MEDIA_BAD_DESTINATION"); + return B_MEDIA_BAD_DESTINATION; // we only have one input so that better be it + } + + if (format == 0) { + fprintf(stderr,"<- B_BAD_VALUE\n"); + return B_BAD_VALUE; // no crashing + } +/* media_format * myFormat = GetFormat(); + fprintf(stderr,"proposed format: "); + print_media_format(format); + fprintf(stderr,"\n"); + fprintf(stderr,"my format: "); + print_media_format(myFormat); + fprintf(stderr,"\n");*/ + // Be's format_is_compatible doesn't work. +// if (!format_is_compatible(*format,*myFormat)) { + + if ( format->type != B_MEDIA_RAW_AUDIO ) { + fprintf(stderr,"<- B_MEDIA_BAD_FORMAT\n"); + return B_MEDIA_BAD_FORMAT; + } + + /*if(format->u.raw_audio.format == media_raw_audio_format::B_AUDIO_FLOAT + && channel->fPreferredFormat.u.raw_audio.format == media_raw_audio_format::B_AUDIO_SHORT) + format->u.raw_audio.format = media_raw_audio_format::B_AUDIO_FLOAT; + else*/ + format->u.raw_audio.format = fPreferredFormat.u.raw_audio.format; + format->u.raw_audio.valid_bits = fPreferredFormat.u.raw_audio.valid_bits; + + format->u.raw_audio.frame_rate = fPreferredFormat.u.raw_audio.frame_rate; + format->u.raw_audio.channel_count = fPreferredFormat.u.raw_audio.channel_count; + format->u.raw_audio.byte_order = B_MEDIA_HOST_ENDIAN; + format->u.raw_audio.buffer_size = ESD_MAX_BUF / 4 +/* * (format->u.raw_audio.format & media_raw_audio_format::B_AUDIO_SIZE_MASK) + * format->u.raw_audio.channel_count*/; + + + /*media_format myFormat; + GetFormat(&myFormat); + if (!format_is_acceptible(*format,myFormat)) { + fprintf(stderr,"<- B_MEDIA_BAD_FORMAT\n"); + return B_MEDIA_BAD_FORMAT; + }*/ + //AddRequirements(format); + return B_OK; +} + +status_t ESDSinkNode::GetNextInput( + int32 * cookie, + media_input * out_input) +{ + CALLED(); + // let's not crash even if they are stupid + if (out_input == 0) { + // no place to write! + fprintf(stderr,"<- B_BAD_VALUE\n"); + return B_BAD_VALUE; + } + + if ((*cookie < 1) && (*cookie >= 0)) { + *out_input = fInput; + *cookie += 1; + PRINT(("input.format : %u\n", fInput.format.u.raw_audio.format)); + return B_OK; + } else + return B_BAD_INDEX; +} + +void ESDSinkNode::DisposeInputCookie( + int32 cookie) +{ + CALLED(); + // nothing to do since our cookies are just integers +} + +void ESDSinkNode::BufferReceived( + BBuffer * buffer) +{ + CALLED(); + switch (buffer->Header()->type) { + /*case B_MEDIA_PARAMETERS: + { + status_t status = ApplyParameterData(buffer->Data(),buffer->SizeUsed()); + if (status != B_OK) { + fprintf(stderr,"ApplyParameterData in ESDSinkNode::BufferReceived failed\n"); + } + buffer->Recycle(); + } + break;*/ + case B_MEDIA_RAW_AUDIO: +#if 0 + if (buffer->Flags() & BBuffer::B_SMALL_BUFFER) { + fprintf(stderr,"NOT IMPLEMENTED: B_SMALL_BUFFER in ESDSinkNode::BufferReceived\n"); + // XXX: implement this part + buffer->Recycle(); + } else { + media_timed_event event(buffer->Header()->start_time, BTimedEventQueue::B_HANDLE_BUFFER, + buffer, BTimedEventQueue::B_RECYCLE_BUFFER); + status_t status = EventQueue()->AddEvent(event); + if (status != B_OK) { + fprintf(stderr,"EventQueue()->AddEvent(event) in ESDSinkNode::BufferReceived failed\n"); + buffer->Recycle(); + } + } +#endif + if (fDevice->CanSend()) { + + fDevice->Write(buffer->Data(), buffer->SizeUsed()); + + } + buffer->Recycle(); + break; + default: + fprintf(stderr,"unexpected buffer type in ESDSinkNode::BufferReceived\n"); + buffer->Recycle(); + break; + } +} + +void ESDSinkNode::ProducerDataStatus( + const media_destination & for_whom, + int32 status, + bigtime_t at_performance_time) +{ + CALLED(); + + if(fInput.destination != for_whom) { + fprintf(stderr,"invalid destination received in ESDSinkNode::ProducerDataStatus\n"); + return; + } + + media_timed_event event(at_performance_time, BTimedEventQueue::B_DATA_STATUS, + &fInput, BTimedEventQueue::B_NO_CLEANUP, status, 0, NULL); + EventQueue()->AddEvent(event); +} + +status_t ESDSinkNode::GetLatencyFor( + const media_destination & for_whom, + bigtime_t * out_latency, + media_node_id * out_timesource) +{ + CALLED(); + if ((out_latency == 0) || (out_timesource == 0)) { + fprintf(stderr,"<- B_BAD_VALUE\n"); + return B_BAD_VALUE; + } + + if(fInput.destination != for_whom) { + fprintf(stderr,"<- B_MEDIA_BAD_DESTINATION\n"); + return B_MEDIA_BAD_DESTINATION; + } + + bigtime_t intl = EventLatency(); + bigtime_t netl = 0LL; + if (fDevice) + netl = fDevice->GetLatency(); + // I don't want to swap + if (netl > 500000) + netl = 500000; + *out_latency = intl + netl; + fprintf(stderr, "int latency %Ld, net latency %Ld, total latency %Ld\n", intl, netl, *out_latency); + *out_timesource = TimeSource()->ID(); + return B_OK; +} + +status_t ESDSinkNode::Connected( + const media_source & producer, /* here's a good place to request buffer group usage */ + const media_destination & where, + const media_format & with_format, + media_input * out_input) +{ + CALLED(); + if (out_input == 0) { + fprintf(stderr,"<- B_BAD_VALUE\n"); + return B_BAD_VALUE; // no crashing + } + + if(fInput.destination != where) { + fprintf(stderr,"<- B_MEDIA_BAD_DESTINATION\n"); + return B_MEDIA_BAD_DESTINATION; + } + + // use one buffer length latency + fInternalLatency = with_format.u.raw_audio.buffer_size * 10000 / 2 + / ( (with_format.u.raw_audio.format & media_raw_audio_format::B_AUDIO_SIZE_MASK) + * with_format.u.raw_audio.channel_count) + / ((int32)(with_format.u.raw_audio.frame_rate / 100)); + + PRINT((" internal latency = %lld\n",fInternalLatency)); + + SetEventLatency(fInternalLatency); + + // record the agreed upon values + fInput.source = producer; + fInput.format = with_format; + *out_input = fInput; + + return B_OK; +} + +void ESDSinkNode::Disconnected( + const media_source & producer, + const media_destination & where) +{ + CALLED(); + + if(fInput.destination != where) { + fprintf(stderr,"<- B_MEDIA_BAD_DESTINATION\n"); + return; + } + if (fInput.source != producer) { + fprintf(stderr,"<- B_MEDIA_BAD_SOURCE\n"); + return; + } + + fInput.source = media_source::null; + fInput.format = fPreferredFormat; + //GetFormat(&channel->fInput.format); +} + + /* The notification comes from the upstream producer, so he's already cool with */ + /* the format; you should not ask him about it in here. */ +status_t ESDSinkNode::FormatChanged( + const media_source & producer, + const media_destination & consumer, + int32 change_tag, + const media_format & format) +{ + CALLED(); + + if(fInput.destination != consumer) { + fprintf(stderr,"<- B_MEDIA_BAD_DESTINATION\n"); + return B_MEDIA_BAD_DESTINATION; + } + if (fInput.source != producer) { + return B_MEDIA_BAD_SOURCE; + } + + return B_ERROR; +} + + /* Given a performance time of some previous buffer, retrieve the remembered tag */ + /* of the closest (previous or exact) performance time. Set *out_flags to 0; the */ + /* idea being that flags can be added later, and the understood flags returned in */ + /* *out_flags. */ +status_t ESDSinkNode::SeekTagRequested( + const media_destination & destination, + bigtime_t in_target_time, + uint32 in_flags, + media_seek_tag * out_seek_tag, + bigtime_t * out_tagged_time, + uint32 * out_flags) +{ + CALLED(); + return BBufferConsumer::SeekTagRequested(destination,in_target_time,in_flags, + out_seek_tag,out_tagged_time,out_flags); +} + +// -------------------------------------------------------- // +// implementation for BBufferProducer +// -------------------------------------------------------- // +#if 0 +status_t +ESDSinkNode::FormatSuggestionRequested(media_type type, int32 /*quality*/, media_format* format) +{ + // FormatSuggestionRequested() is not necessarily part of the format negotiation + // process; it's simply an interrogation -- the caller wants to see what the node's + // preferred data format is, given a suggestion by the caller. + CALLED(); + + if (!format) + { + fprintf(stderr, "\tERROR - NULL format pointer passed in!\n"); + return B_BAD_VALUE; + } + + // this is the format we'll be returning (our preferred format) + *format = fPreferredFormat; + + // a wildcard type is okay; we can specialize it + if (type == B_MEDIA_UNKNOWN_TYPE) type = B_MEDIA_RAW_AUDIO; + + // we only support raw audio + if (type != B_MEDIA_RAW_AUDIO) return B_MEDIA_BAD_FORMAT; + else return B_OK; +} + +status_t +ESDSinkNode::FormatProposal(const media_source& output, media_format* format) +{ + // FormatProposal() is the first stage in the BMediaRoster::Connect() process. We hand + // out a suggested format, with wildcards for any variations we support. + CALLED(); + node_output *channel = FindOutput(output); + + // is this a proposal for our select output? + if (channel == NULL) + { + fprintf(stderr, "ESDSinkNode::FormatProposal returning B_MEDIA_BAD_SOURCE\n"); + return B_MEDIA_BAD_SOURCE; + } + + // we only support floating-point raw audio, so we always return that, but we + // supply an error code depending on whether we found the proposal acceptable. + media_type requestedType = format->type; + *format = channel->fPreferredFormat; + if ((requestedType != B_MEDIA_UNKNOWN_TYPE) && (requestedType != B_MEDIA_RAW_AUDIO)) + { + fprintf(stderr, "ESDSinkNode::FormatProposal returning B_MEDIA_BAD_FORMAT\n"); + return B_MEDIA_BAD_FORMAT; + } + else return B_OK; // raw audio or wildcard type, either is okay by us +} + +status_t +ESDSinkNode::FormatChangeRequested(const media_source& source, const media_destination& destination, media_format* io_format, int32* _deprecated_) +{ + CALLED(); + + // we don't support any other formats, so we just reject any format changes. + return B_ERROR; +} + +status_t +ESDSinkNode::GetNextOutput(int32* cookie, media_output* out_output) +{ + CALLED(); + + if ((*cookie < fOutputs.CountItems()) && (*cookie >= 0)) { + node_output *channel = (node_output *)fOutputs.ItemAt(*cookie); + *out_output = channel->fOutput; + *cookie += 1; + return B_OK; + } else + return B_BAD_INDEX; +} + +status_t +ESDSinkNode::DisposeOutputCookie(int32 cookie) +{ + CALLED(); + // do nothing because we don't use the cookie for anything special + return B_OK; +} + +status_t +ESDSinkNode::SetBufferGroup(const media_source& for_source, BBufferGroup* newGroup) +{ + CALLED(); + + node_output *channel = FindOutput(for_source); + + // is this our output? + if (channel == NULL) + { + fprintf(stderr, "ESDSinkNode::SetBufferGroup returning B_MEDIA_BAD_SOURCE\n"); + return B_MEDIA_BAD_SOURCE; + } + + // Are we being passed the buffer group we're already using? + if (newGroup == channel->fBufferGroup) return B_OK; + + // Ahh, someone wants us to use a different buffer group. At this point we delete + // the one we are using and use the specified one instead. If the specified group is + // NULL, we need to recreate one ourselves, and use *that*. Note that if we're + // caching a BBuffer that we requested earlier, we have to Recycle() that buffer + // *before* deleting the buffer group, otherwise we'll deadlock waiting for that + // buffer to be recycled! + delete channel->fBufferGroup; // waits for all buffers to recycle + if (newGroup != NULL) + { + // we were given a valid group; just use that one from now on + channel->fBufferGroup = newGroup; + } + else + { + // we were passed a NULL group pointer; that means we construct + // our own buffer group to use from now on + size_t size = channel->fOutput.format.u.raw_audio.buffer_size; + int32 count = int32(fLatency / BufferDuration() + 1 + 1); + channel->fBufferGroup = new BBufferGroup(size, count); + } + + return B_OK; +} + +status_t +ESDSinkNode::PrepareToConnect(const media_source& what, const media_destination& where, media_format* format, media_source* out_source, char* out_name) +{ + // PrepareToConnect() is the second stage of format negotiations that happens + // inside BMediaRoster::Connect(). At this point, the consumer's AcceptFormat() + // method has been called, and that node has potentially changed the proposed + // format. It may also have left wildcards in the format. PrepareToConnect() + // *must* fully specialize the format before returning! + CALLED(); + + node_output *channel = FindOutput(what); + + // is this our output? + if (channel == NULL) + { + fprintf(stderr, "ESDSinkNode::PrepareToConnect returning B_MEDIA_BAD_SOURCE\n"); + return B_MEDIA_BAD_SOURCE; + } + + // are we already connected? + if (channel->fOutput.destination != media_destination::null) + return B_MEDIA_ALREADY_CONNECTED; + + // the format may not yet be fully specialized (the consumer might have + // passed back some wildcards). Finish specializing it now, and return an + // error if we don't support the requested format. + if (format->type != B_MEDIA_RAW_AUDIO) + { + fprintf(stderr, "\tnon-raw-audio format?!\n"); + return B_MEDIA_BAD_FORMAT; + } + + // !!! validate all other fields except for buffer_size here, because the consumer might have + // supplied different values from AcceptFormat()? + + // check the buffer size, which may still be wildcarded + if (format->u.raw_audio.buffer_size == media_raw_audio_format::wildcard.buffer_size) + { + format->u.raw_audio.buffer_size = 2048; // pick something comfortable to suggest + fprintf(stderr, "\tno buffer size provided, suggesting %lu\n", format->u.raw_audio.buffer_size); + } + else + { + fprintf(stderr, "\tconsumer suggested buffer_size %lu\n", format->u.raw_audio.buffer_size); + } + + // Now reserve the connection, and return information about it + channel->fOutput.destination = where; + channel->fOutput.format = *format; + *out_source = channel->fOutput.source; + strncpy(out_name, channel->fOutput.name, B_MEDIA_NAME_LENGTH); + return B_OK; +} + +void +ESDSinkNode::Connect(status_t error, const media_source& source, const media_destination& destination, const media_format& format, char* io_name) +{ + CALLED(); + + node_output *channel = FindOutput(source); + + // is this our output? + if (channel == NULL) + { + fprintf(stderr, "ESDSinkNode::Connect returning (cause : B_MEDIA_BAD_SOURCE)\n"); + return; + } + + // If something earlier failed, Connect() might still be called, but with a non-zero + // error code. When that happens we simply unreserve the connection and do + // nothing else. + if (error) + { + channel->fOutput.destination = media_destination::null; + channel->fOutput.format = channel->fPreferredFormat; + return; + } + + // Okay, the connection has been confirmed. Record the destination and format + // that we agreed on, and report our connection name again. + channel->fOutput.destination = destination; + channel->fOutput.format = format; + strncpy(io_name, channel->fOutput.name, B_MEDIA_NAME_LENGTH); + + // reset our buffer duration, etc. to avoid later calculations + bigtime_t duration = channel->fOutput.format.u.raw_audio.buffer_size * 10000 + / ( (channel->fOutput.format.u.raw_audio.format & media_raw_audio_format::B_AUDIO_SIZE_MASK) + * channel->fOutput.format.u.raw_audio.channel_count) + / ((int32)(channel->fOutput.format.u.raw_audio.frame_rate / 100)); + + SetBufferDuration(duration); + + // Now that we're connected, we can determine our downstream latency. + // Do so, then make sure we get our events early enough. + media_node_id id; + FindLatencyFor(channel->fOutput.destination, &fLatency, &id); + PRINT(("\tdownstream latency = %Ld\n", fLatency)); + + fInternalLatency = BufferDuration(); + PRINT(("\tbuffer-filling took %Ld usec on this machine\n", fInternalLatency)); + //SetEventLatency(fLatency + fInternalLatency); + + // Set up the buffer group for our connection, as long as nobody handed us a + // buffer group (via SetBufferGroup()) prior to this. That can happen, for example, + // if the consumer calls SetOutputBuffersFor() on us from within its Connected() + // method. + if (!channel->fBufferGroup) + AllocateBuffers(*channel); + + // we are sure the thread is started + StartThread(); +} + +void +ESDSinkNode::Disconnect(const media_source& what, const media_destination& where) +{ + CALLED(); + + node_output *channel = FindOutput(what); + + // is this our output? + if (channel == NULL) + { + fprintf(stderr, "ESDSinkNode::Disconnect() returning (cause : B_MEDIA_BAD_SOURCE)\n"); + return; + } + + // Make sure that our connection is the one being disconnected + if ((where == channel->fOutput.destination) && (what == channel->fOutput.source)) + { + channel->fOutput.destination = media_destination::null; + channel->fOutput.format = channel->fPreferredFormat; + delete channel->fBufferGroup; + channel->fBufferGroup = NULL; + } + else + { + fprintf(stderr, "\tDisconnect() called with wrong source/destination (%ld/%ld), ours is (%ld/%ld)\n", + what.id, where.id, channel->fOutput.source.id, channel->fOutput.destination.id); + } +} + +void +ESDSinkNode::LateNoticeReceived(const media_source& what, bigtime_t how_much, bigtime_t performance_time) +{ + CALLED(); + + node_output *channel = FindOutput(what); + + // is this our output? + if (channel == NULL) + { + return; + } + + // If we're late, we need to catch up. Respond in a manner appropriate to our + // current run mode. + if (RunMode() == B_RECORDING) + { + // A hardware capture node can't adjust; it simply emits buffers at + // appropriate points. We (partially) simulate this by not adjusting + // our behavior upon receiving late notices -- after all, the hardware + // can't choose to capture "sooner".... + } + else if (RunMode() == B_INCREASE_LATENCY) + { + // We're late, and our run mode dictates that we try to produce buffers + // earlier in order to catch up. This argues that the downstream nodes are + // not properly reporting their latency, but there's not much we can do about + // that at the moment, so we try to start producing buffers earlier to + // compensate. + fInternalLatency += how_much; + SetEventLatency(fLatency + fInternalLatency); + + fprintf(stderr, "\tincreasing latency to %Ld\n", fLatency + fInternalLatency); + } + else + { + // The other run modes dictate various strategies for sacrificing data quality + // in the interests of timely data delivery. The way *we* do this is to skip + // a buffer, which catches us up in time by one buffer duration. + /*size_t nSamples = fOutput.format.u.raw_audio.buffer_size / sizeof(float); + mSamplesSent += nSamples;*/ + + fprintf(stderr, "\tskipping a buffer to try to catch up\n"); + } +} + +void +ESDSinkNode::EnableOutput(const media_source& what, bool enabled, int32* _deprecated_) +{ + CALLED(); + + // If I had more than one output, I'd have to walk my list of output records to see + // which one matched the given source, and then enable/disable that one. But this + // node only has one output, so I just make sure the given source matches, then set + // the enable state accordingly. + node_output *channel = FindOutput(what); + + if (channel != NULL) + { + channel->fOutputEnabled = enabled; + } +} + +void +ESDSinkNode::AdditionalBufferRequested(const media_source& source, media_buffer_id prev_buffer, bigtime_t prev_time, const media_seek_tag* prev_tag) +{ + CALLED(); + // we don't support offline mode + return; +} +#endif + +// -------------------------------------------------------- // +// implementation for BMediaEventLooper +// -------------------------------------------------------- // + +void ESDSinkNode::HandleEvent( + const media_timed_event *event, + bigtime_t lateness, + bool realTimeEvent) +{ + CALLED(); + switch (event->type) { + case BTimedEventQueue::B_START: + HandleStart(event,lateness,realTimeEvent); + break; + case BTimedEventQueue::B_SEEK: + HandleSeek(event,lateness,realTimeEvent); + break; + case BTimedEventQueue::B_WARP: + HandleWarp(event,lateness,realTimeEvent); + break; + case BTimedEventQueue::B_STOP: + HandleStop(event,lateness,realTimeEvent); + break; + case BTimedEventQueue::B_HANDLE_BUFFER: + if (RunState() == BMediaEventLooper::B_STARTED) { + HandleBuffer(event,lateness,realTimeEvent); + } + break; + case BTimedEventQueue::B_DATA_STATUS: + HandleDataStatus(event,lateness,realTimeEvent); + break; + case BTimedEventQueue::B_PARAMETER: + HandleParameter(event,lateness,realTimeEvent); + break; + default: + fprintf(stderr," unknown event type: %li\n",event->type); + break; + } +} + +// protected: + +// how should we handle late buffers? drop them? +// notify the producer? +status_t ESDSinkNode::HandleBuffer( + const media_timed_event *event, + bigtime_t lateness, + bool realTimeEvent) +{ + CALLED(); + BBuffer * buffer = const_cast((BBuffer*)event->pointer); + if (buffer == 0) { + fprintf(stderr,"<- B_BAD_VALUE\n"); + return B_BAD_VALUE; + } + + if(fInput.destination.id != buffer->Header()->destination) { + fprintf(stderr,"<- B_MEDIA_BAD_DESTINATION\n"); + return B_MEDIA_BAD_DESTINATION; + } + + media_header* hdr = buffer->Header(); + bigtime_t now = TimeSource()->Now(); + bigtime_t perf_time = hdr->start_time; + + // the how_early calculate here doesn't include scheduling latency because + // we've already been scheduled to handle the buffer + bigtime_t how_early = perf_time - EventLatency() - now; + + // if the buffer is late, we ignore it and report the fact to the producer + // who sent it to us + if ((RunMode() != B_OFFLINE) && // lateness doesn't matter in offline mode... + (RunMode() != B_RECORDING) && // ...or in recording mode + (how_early < 0LL)) + { + //mLateBuffers++; + NotifyLateProducer(fInput.source, -how_early, perf_time); + fprintf(stderr," <- LATE BUFFER : %lli\n", how_early); + buffer->Recycle(); + } else { + if (fDevice->CanSend()) + fDevice->Write(buffer->Data(), buffer->SizeUsed()); + } + return B_OK; +} + +status_t ESDSinkNode::HandleDataStatus( + const media_timed_event *event, + bigtime_t lateness, + bool realTimeEvent) +{ + CALLED(); + PRINT(("ESDSinkNode::HandleDataStatus status:%li, lateness:%li\n", event->data, lateness)); + switch(event->data) { + case B_DATA_NOT_AVAILABLE: + break; + case B_DATA_AVAILABLE: + break; + case B_PRODUCER_STOPPED: + break; + default: + break; + } + return B_OK; +} + +status_t ESDSinkNode::HandleStart( + const media_timed_event *event, + bigtime_t lateness, + bool realTimeEvent) +{ + CALLED(); + if (RunState() != B_STARTED) { + + } + return B_OK; +} + +status_t ESDSinkNode::HandleSeek( + const media_timed_event *event, + bigtime_t lateness, + bool realTimeEvent) +{ + CALLED(); + PRINT(("ESDSinkNode::HandleSeek(t=%lld,d=%li,bd=%lld)\n",event->event_time,event->data,event->bigdata)); + return B_OK; +} + +status_t ESDSinkNode::HandleWarp( + const media_timed_event *event, + bigtime_t lateness, + bool realTimeEvent) +{ + CALLED(); + return B_OK; +} + +status_t ESDSinkNode::HandleStop( + const media_timed_event *event, + bigtime_t lateness, + bool realTimeEvent) +{ + CALLED(); + // flush the queue so downstreamers don't get any more + EventQueue()->FlushEvents(0, BTimedEventQueue::B_ALWAYS, true, BTimedEventQueue::B_HANDLE_BUFFER); + + //StopThread(); + return B_OK; +} + +status_t ESDSinkNode::HandleParameter( + const media_timed_event *event, + bigtime_t lateness, + bool realTimeEvent) +{ + CALLED(); + return B_OK; +} + +// -------------------------------------------------------- // +// implemention of BTimeSource +// -------------------------------------------------------- // +#ifdef ENABLE_TS + +void +ESDSinkNode::SetRunMode(run_mode mode) +{ + CALLED(); + PRINT(("ESDSinkNode::SetRunMode mode:%i\n", mode)); + //BTimeSource::SetRunMode(mode); +} + +status_t +ESDSinkNode::TimeSourceOp(const time_source_op_info &op, void *_reserved) +{ + CALLED(); + switch(op.op) { + case B_TIMESOURCE_START: + PRINT(("TimeSourceOp op B_TIMESOURCE_START\n")); + if (RunState() != BMediaEventLooper::B_STARTED) { + fTimeSourceStarted = true; + + media_timed_event startEvent(0, BTimedEventQueue::B_START); + EventQueue()->AddEvent(startEvent); + } + break; + case B_TIMESOURCE_STOP: + PRINT(("TimeSourceOp op B_TIMESOURCE_STOP\n")); + if (RunState() == BMediaEventLooper::B_STARTED) { + media_timed_event stopEvent(0, BTimedEventQueue::B_STOP); + EventQueue()->AddEvent(stopEvent); + fTimeSourceStarted = false; + PublishTime(0, 0, 0); + } + break; + case B_TIMESOURCE_STOP_IMMEDIATELY: + PRINT(("TimeSourceOp op B_TIMESOURCE_STOP_IMMEDIATELY\n")); + if (RunState() == BMediaEventLooper::B_STARTED) { + media_timed_event stopEvent(0, BTimedEventQueue::B_STOP); + EventQueue()->AddEvent(stopEvent); + fTimeSourceStarted = false; + PublishTime(0, 0, 0); + } + break; + case B_TIMESOURCE_SEEK: + PRINT(("TimeSourceOp op B_TIMESOURCE_SEEK\n")); + BroadcastTimeWarp(op.real_time, op.performance_time); + break; + default: + break; + } + return B_OK; +} +#endif + +// -------------------------------------------------------- // +// implemention of BControllable +// -------------------------------------------------------- // + +status_t +ESDSinkNode::GetParameterValue(int32 id, bigtime_t* last_change, void* value, size_t* ioSize) +{ + CALLED(); + + PRINT(("id : %i\n", id)); + BParameter *parameter = NULL; + for(int32 i=0; iCountParameters(); i++) { + parameter = fWeb->ParameterAt(i); + if(parameter->ID() == id) + break; + } +#if 0 + if(!parameter) { + // Hmmm, we were asked for a parameter that we don't actually + // support. Report an error back to the caller. + PRINT(("\terror - asked for illegal parameter %ld\n", id)); + return B_ERROR; + } + + multi_mix_value_info MMVI; + multi_mix_value MMV[2]; + int rval; + MMVI.values = MMV; + id = id - 100; + MMVI.item_count = 0; + + if (*ioSize < sizeof(float)) + return B_ERROR; + + if(parameter->Type() == BParameter::B_CONTINUOUS_PARAMETER) { + MMVI.item_count = 1; + MMV[0].id = id; + + if(parameter->CountChannels() == 2) { + if (*ioSize < 2*sizeof(float)) + return B_ERROR; + MMVI.item_count = 2; + MMV[1].id = id + 1; + } + + } else if(parameter->Type() == BParameter::B_DISCRETE_PARAMETER) { + MMVI.item_count = 1; + MMV[0].id = id; + } + + if(MMVI.item_count > 0) { + rval = fDevice->DoGetMix(&MMVI); + + if (B_OK != rval) { + fprintf(stderr, "Failed on DRIVER_GET_MIX\n"); + } else { + + if(parameter->Type() == BParameter::B_CONTINUOUS_PARAMETER) { + ((float*)value)[0] = MMV[0].gain; + *ioSize = sizeof(float); + + if(parameter->CountChannels() == 2) { + ((float*)value)[1] = MMV[1].gain; + *ioSize = 2*sizeof(float); + } + + for(uint32 i=0; i < (*ioSize/sizeof(float)); i++) { + PRINT(("B_CONTINUOUS_PARAMETER value[%i] : %f\n", i, ((float*)value)[i])); + } + } else if(parameter->Type() == BParameter::B_DISCRETE_PARAMETER) { + + BDiscreteParameter *dparameter = (BDiscreteParameter*) parameter; + if(dparameter->CountItems()<=2) { + ((int32*)value)[0] = (MMV[0].enable) ? 1 : 0; + } else { + ((int32*)value)[0] = MMV[0].mux; + } + *ioSize = sizeof(int32); + + for(uint32 i=0; i < (*ioSize/sizeof(int32)); i++) { + PRINT(("B_DISCRETE_PARAMETER value[%i] : %i\n", i, ((int32*)value)[i])); + } + } + + } + } + return B_OK; +#endif +return EINVAL; +} + +void +ESDSinkNode::SetParameterValue(int32 id, bigtime_t performance_time, const void* value, size_t size) +{ + CALLED(); + PRINT(("id : %i, performance_time : %lld, size : %i\n", id, performance_time, size)); + BParameter *parameter = NULL; + for(int32 i=0; iCountParameters(); i++) { + parameter = fWeb->ParameterAt(i); + if(parameter->ID() == id) + break; + } +#if 0 + if(parameter) { + multi_mix_value_info MMVI; + multi_mix_value MMV[2]; + int rval; + MMVI.values = MMV; + id = id - 100; + MMVI.item_count = 0; + + if(parameter->Type() == BParameter::B_CONTINUOUS_PARAMETER) { + for(uint32 i=0; i < (size/sizeof(float)); i++) { + PRINT(("B_CONTINUOUS_PARAMETER value[%i] : %f\n", i, ((float*)value)[i])); + } + MMVI.item_count = 1; + MMV[0].id = id; + MMV[0].gain = ((float*)value)[0]; + + if(parameter->CountChannels() == 2) { + MMVI.item_count = 2; + MMV[1].id = id + 1; + MMV[1].gain = ((float*)value)[1]; + } + + } else if(parameter->Type() == BParameter::B_DISCRETE_PARAMETER) { + for(uint32 i=0; i < (size/sizeof(int32)); i++) { + PRINT(("B_DISCRETE_PARAMETER value[%i] : %i\n", i, ((int32*)value)[i])); + } + BDiscreteParameter *dparameter = (BDiscreteParameter*) parameter; + + if(dparameter->CountItems()<=2) { + MMVI.item_count = 1; + MMV[0].id = id; + MMV[0].enable = (((int32*)value)[0] == 1) ? true : false; + } else { + MMVI.item_count = 1; + MMV[0].id = id; + MMV[0].mux = ((uint32*)value)[0]; + } + } + + if(MMVI.item_count > 0) { + rval = fDevice->DoSetMix(&MMVI); + + if (B_OK != rval) + { + fprintf(stderr, "Failed on DRIVER_SET_MIX\n"); + } + } + } +#endif +} + +BParameterWeb* +ESDSinkNode::MakeParameterWeb() +{ + CALLED(); + BParameterWeb* web = new BParameterWeb; +#if 0 + PRINT(("MMCI.control_count : %i\n", fDevice->MMCI.control_count)); + multi_mix_control *MMC = fDevice->MMCI.controls; + + for(int i=0; iMMCI.control_count; i++) { + if(MMC[i].flags & B_MULTI_MIX_GROUP && MMC[i].parent == 0) { + PRINT(("NEW_GROUP\n")); + int32 nb = 0; + const char* childName; + if(MMC[i].string != S_null) + childName = multi_string[MMC[i].string]; + else + childName = MMC[i].name; + BParameterGroup *child = web->MakeGroup(childName); + ProcessGroup(child, i, nb); + } + } +#endif + int id = 0; + BParameterGroup *group = web->MakeGroup("Server"); + BParameter *p; + p = group->MakeTextParameter(id++, B_MEDIA_RAW_AUDIO, "Hostname", B_GENERIC, 128); + p = group->MakeTextParameter(id++, B_MEDIA_RAW_AUDIO, "Port", B_GENERIC, 16); + return web; +} +#if 0 +void +ESDSinkNode::ProcessGroup(BParameterGroup *group, int32 index, int32 &nbParameters) +{ + CALLED(); + multi_mix_control *parent = &fDevice->MMCI.controls[index]; + multi_mix_control *MMC = fDevice->MMCI.controls; + for(int32 i=0; iMMCI.control_count; i++) { + if(MMC[i].parent != parent->id) + continue; + + const char* childName; + if(MMC[i].string != S_null) + childName = multi_string[MMC[i].string]; + else + childName = MMC[i].name; + + if(MMC[i].flags & B_MULTI_MIX_GROUP) { + PRINT(("NEW_GROUP\n")); + int32 nb = 1; + BParameterGroup *child = group->MakeGroup(childName); + child->MakeNullParameter(MMC[i].id, B_MEDIA_RAW_AUDIO, childName, B_WEB_BUFFER_OUTPUT); + ProcessGroup(child, i, nb); + } else if(MMC[i].flags & B_MULTI_MIX_MUX) { + PRINT(("NEW_MUX\n")); + BDiscreteParameter *parameter = + group->MakeDiscreteParameter(100 + MMC[i].id, B_MEDIA_RAW_AUDIO, childName, B_INPUT_MUX); + if(nbParameters>0) { + (group->ParameterAt(nbParameters - 1))->AddOutput(group->ParameterAt(nbParameters)); + nbParameters++; + } + ProcessMux(parameter, i); + } else if(MMC[i].flags & B_MULTI_MIX_GAIN) { + PRINT(("NEW_GAIN\n")); + group->MakeContinuousParameter(100 + MMC[i].id, B_MEDIA_RAW_AUDIO, "", B_MASTER_GAIN, + "dB", MMC[i].gain.min_gain, MMC[i].gain.max_gain, MMC[i].gain.granularity); + + if(i+1 MMCI.control_count && MMC[i+1].master == MMC[i].id && MMC[i+1].flags & B_MULTI_MIX_GAIN) { + group->ParameterAt(nbParameters)->SetChannelCount( + group->ParameterAt(nbParameters)->CountChannels() + 1); + i++; + } + + PRINT(("nb parameters : %d\n", nbParameters)); + if (nbParameters > 0) { + (group->ParameterAt(nbParameters - 1))->AddOutput(group->ParameterAt(nbParameters)); + nbParameters++; + } + } else if(MMC[i].flags & B_MULTI_MIX_ENABLE) { + PRINT(("NEW_ENABLE\n")); + if(MMC[i].string == S_MUTE) + group->MakeDiscreteParameter(100 + MMC[i].id, B_MEDIA_RAW_AUDIO, childName, B_MUTE); + else + group->MakeDiscreteParameter(100 + MMC[i].id, B_MEDIA_RAW_AUDIO, childName, B_ENABLE); + if(nbParameters>0) { + (group->ParameterAt(nbParameters - 1))->AddOutput(group->ParameterAt(nbParameters)); + nbParameters++; + } + } + } +} + +void +ESDSinkNode::ProcessMux(BDiscreteParameter *parameter, int32 index) +{ + CALLED(); + multi_mix_control *parent = &fDevice->MMCI.controls[index]; + multi_mix_control *MMC = fDevice->MMCI.controls; + int32 itemIndex = 0; + for(int32 i=0; iMMCI.control_count; i++) { + if(MMC[i].parent != parent->id) + continue; + + const char* childName; + if(MMC[i].string != S_null) + childName = multi_string[MMC[i].string]; + else + childName = MMC[i].name; + + if(MMC[i].flags & B_MULTI_MIX_MUX_VALUE) { + PRINT(("NEW_MUX_VALUE\n")); + parameter->AddItem(itemIndex, childName); + itemIndex++; + } + } +} +#endif +// -------------------------------------------------------- // +// ESDSinkNode specific functions +// -------------------------------------------------------- // + +status_t +ESDSinkNode::GetConfigurationFor(BMessage * into_message) +{ + CALLED(); + + BParameter *parameter = NULL; + void *buffer; + size_t size = 128; + bigtime_t last_change; + status_t err; + + if(!into_message) + return B_BAD_VALUE; + + buffer = malloc(size); + + for(int32 i=0; iCountParameters(); i++) { + parameter = fWeb->ParameterAt(i); + if(parameter->Type() != BParameter::B_CONTINUOUS_PARAMETER + && parameter->Type() != BParameter::B_DISCRETE_PARAMETER) + continue; + + PRINT(("getting parameter %i\n", parameter->ID())); + size = 128; + while((err = GetParameterValue(parameter->ID(), &last_change, buffer, &size))==B_NO_MEMORY) { + size += 128; + free(buffer); + buffer = malloc(size); + } + + if(err == B_OK && size > 0) { + into_message->AddInt32("parameterID", parameter->ID()); + into_message->AddData("parameterData", B_RAW_TYPE, buffer, size, false); + } else { + PRINT(("parameter err : %s\n", strerror(err))); + } + } + + //PRINT_OBJECT(*into_message); + + return B_OK; +} + +// static: + +void ESDSinkNode::GetFlavor(flavor_info * outInfo, int32 id) +{ + CALLED(); + if (outInfo == 0) { + return; + } + + outInfo->flavor_flags = B_FLAVOR_IS_GLOBAL; +// outInfo->possible_count = 0; // any number + outInfo->possible_count = 1; // only 1 + outInfo->in_format_count = 0; // no inputs + outInfo->in_formats = 0; + outInfo->out_format_count = 0; // no outputs + outInfo->out_formats = 0; + outInfo->internal_id = id; + + outInfo->name = new char[256]; + strcpy(outInfo->name, "ESounD Out"); + outInfo->info = new char[256]; + strcpy(outInfo->info, "The ESounD Sink node outputs a network Enlightenment Sound Daemon."); + outInfo->kinds = /*B_TIME_SOURCE | *//*B_CONTROLLABLE | */ 0; + +#if ENABLE_INPUT + outInfo->kinds |= B_BUFFER_PRODUCER | B_PHYSICAL_INPUT; + outInfo->out_format_count = 1; // 1 output + media_format * outformats = new media_format[outInfo->out_format_count]; + GetFormat(&outformats[0]); + outInfo->out_formats = outformats; +#endif + + outInfo->kinds |= B_BUFFER_CONSUMER | B_PHYSICAL_OUTPUT; + outInfo->in_format_count = 1; // 1 input + media_format * informats = new media_format[outInfo->in_format_count]; + GetFormat(&informats[0]); + outInfo->in_formats = informats; +} + +void ESDSinkNode::GetFormat(media_format * outFormat) +{ + CALLED(); + if (outFormat == 0) { + return; + } + outFormat->type = B_MEDIA_RAW_AUDIO; + outFormat->require_flags = B_MEDIA_MAUI_UNDEFINED_FLAGS; + outFormat->deny_flags = B_MEDIA_MAUI_UNDEFINED_FLAGS; + outFormat->u.raw_audio = media_raw_audio_format::wildcard; +} diff --git a/src/add-ons/media/media-add-ons/esound_sink/ESDSinkNode.h b/src/add-ons/media/media-add-ons/esound_sink/ESDSinkNode.h new file mode 100644 index 0000000000..1721263399 --- /dev/null +++ b/src/add-ons/media/media-add-ons/esound_sink/ESDSinkNode.h @@ -0,0 +1,364 @@ +/* + * ESounD media addon for BeOS + * + * Copyright (c) 2006 François Revol (revol@free.fr) + * + * Based on Multi Audio addon for Haiku, + * Copyright (c) 2002, 2003 Jerome Duval (jerome.duval@free.fr) + * + * All rights reserved. + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * - Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * - Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ +#ifndef _ESDSINK_NODE_H +#define _ESDSINK_NODE_H + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "ESDEndpoint.h" + +//#define ENABLE_INPUT 1 +//#define ENABLE_TS 1 + +/*bool format_is_acceptible( + const media_format & producer_format, + const media_format & consumer_format);*/ + +class ESDSinkNode : + public BBufferConsumer, +#if ENABLE_INPUT + public BBufferProducer, +#endif +#ifdef ENABLE_TS + public BTimeSource, +#endif + public BMediaEventLooper, + public BControllable +{ +protected: +virtual ~ESDSinkNode(void); + +public: + +explicit ESDSinkNode(BMediaAddOn *addon, char* name, BMessage * config); + +virtual status_t InitCheck(void) const; + +/*************************/ +/* begin from BMediaNode */ +public: +virtual BMediaAddOn* AddOn( + int32 * internal_id) const; /* Who instantiated you -- or NULL for app class */ + +protected: + /* These don't return errors; instead, they use the global error condition reporter. */ + /* A node is required to have a queue of at least one pending command (plus TimeWarp) */ + /* and is recommended to allow for at least one pending command of each type. */ + /* Allowing an arbitrary number of outstanding commands might be nice, but apps */ + /* cannot depend on that happening. */ +virtual void Preroll(void); + +public: +virtual status_t HandleMessage( + int32 message, + const void * data, + size_t size); + +protected: +virtual void NodeRegistered(void); /* reserved 2 */ +virtual status_t RequestCompleted(const media_request_info &info); +virtual void SetTimeSource(BTimeSource *timeSource); + +/* end from BMediaNode */ +/***********************/ + +/******************************/ +/* begin from BBufferConsumer */ + +//included from BMediaAddOn +//virtual status_t HandleMessage( +// int32 message, +// const void * data, +// size_t size); + + /* Someone, probably the producer, is asking you about this format. Give */ + /* your honest opinion, possibly modifying *format. Do not ask upstream */ + /* producer about the format, since he's synchronously waiting for your */ + /* reply. */ +virtual status_t AcceptFormat( + const media_destination & dest, + media_format * format); +virtual status_t GetNextInput( + int32 * cookie, + media_input * out_input); +virtual void DisposeInputCookie( + int32 cookie); +virtual void BufferReceived( + BBuffer * buffer); +virtual void ProducerDataStatus( + const media_destination & for_whom, + int32 status, + bigtime_t at_performance_time); +virtual status_t GetLatencyFor( + const media_destination & for_whom, + bigtime_t * out_latency, + media_node_id * out_timesource); +virtual status_t Connected( + const media_source & producer, /* here's a good place to request buffer group usage */ + const media_destination & where, + const media_format & with_format, + media_input * out_input); +virtual void Disconnected( + const media_source & producer, + const media_destination & where); + /* The notification comes from the upstream producer, so he's already cool with */ + /* the format; you should not ask him about it in here. */ +virtual status_t FormatChanged( + const media_source & producer, + const media_destination & consumer, + int32 change_tag, + const media_format & format); + + /* Given a performance time of some previous buffer, retrieve the remembered tag */ + /* of the closest (previous or exact) performance time. Set *out_flags to 0; the */ + /* idea being that flags can be added later, and the understood flags returned in */ + /* *out_flags. */ +virtual status_t SeekTagRequested( + const media_destination & destination, + bigtime_t in_target_time, + uint32 in_flags, + media_seek_tag * out_seek_tag, + bigtime_t * out_tagged_time, + uint32 * out_flags); + +/* end from BBufferConsumer */ +/****************************/ + +/******************************/ +/* begin from BBufferProducer */ +#if 0 + virtual status_t FormatSuggestionRequested( media_type type, + int32 quality, + media_format* format); + + virtual status_t FormatProposal( const media_source& output, + media_format* format); + + virtual status_t FormatChangeRequested( const media_source& source, + const media_destination& destination, + media_format* io_format, + int32* _deprecated_); + virtual status_t GetNextOutput( int32* cookie, + media_output* out_output); + virtual status_t DisposeOutputCookie( int32 cookie); + + virtual status_t SetBufferGroup( const media_source& for_source, + BBufferGroup* group); + + virtual status_t PrepareToConnect( const media_source& what, + const media_destination& where, + media_format* format, + media_source* out_source, + char* out_name); + + virtual void Connect( status_t error, + const media_source& source, + const media_destination& destination, + const media_format& format, + char* io_name); + + virtual void Disconnect( const media_source& what, + const media_destination& where); + + virtual void LateNoticeReceived( const media_source& what, + bigtime_t how_much, + bigtime_t performance_time); + + virtual void EnableOutput( const media_source & what, + bool enabled, + int32* _deprecated_); + virtual void AdditionalBufferRequested( const media_source& source, + media_buffer_id prev_buffer, + bigtime_t prev_time, + const media_seek_tag* prev_tag); +#endif +/* end from BBufferProducer */ +/****************************/ + +/*****************/ +/* BControllable */ +/*****************/ + +/********************************/ +/* start from BMediaEventLooper */ + + protected: + /* you must override to handle your events! */ + /* you should not call HandleEvent directly */ + virtual void HandleEvent( const media_timed_event *event, + bigtime_t lateness, + bool realTimeEvent = false); + +/* end from BMediaEventLooper */ +/******************************/ + +/********************************/ +/* start from BTimeSource */ +#ifdef ENABLE_TS + protected: + virtual void SetRunMode( run_mode mode); + virtual status_t TimeSourceOp( const time_source_op_info &op, + void *_reserved); +#endif +/* end from BTimeSource */ +/******************************/ + +/********************************/ +/* start from BControllable */ + protected: + virtual status_t GetParameterValue( int32 id, + bigtime_t* last_change, + void* value, + size_t* ioSize); + virtual void SetParameterValue( int32 id, + bigtime_t when, + const void* value, + size_t size); + virtual BParameterWeb* MakeParameterWeb(); + +/* end from BControllable */ +/******************************/ + +protected: + +virtual status_t HandleStart( + const media_timed_event *event, + bigtime_t lateness, + bool realTimeEvent = false); +virtual status_t HandleSeek( + const media_timed_event *event, + bigtime_t lateness, + bool realTimeEvent = false); +virtual status_t HandleWarp( + const media_timed_event *event, + bigtime_t lateness, + bool realTimeEvent = false); +virtual status_t HandleStop( + const media_timed_event *event, + bigtime_t lateness, + bool realTimeEvent = false); +virtual status_t HandleBuffer( + const media_timed_event *event, + bigtime_t lateness, + bool realTimeEvent = false); +virtual status_t HandleDataStatus( + const media_timed_event *event, + bigtime_t lateness, + bool realTimeEvent = false); +virtual status_t HandleParameter( + const media_timed_event *event, + bigtime_t lateness, + bool realTimeEvent = false); + +public: + +static void GetFlavor(flavor_info * outInfo, int32 id); +static void GetFormat(media_format * outFormat); + +status_t GetConfigurationFor(BMessage * into_message); + + +private: + + ESDSinkNode( /* private unimplemented */ + const ESDSinkNode & clone); + ESDSinkNode & operator=( + const ESDSinkNode & clone); + +#if 0 + + void AllocateBuffers(node_output &channel); + BBuffer* FillNextBuffer( multi_buffer_info &MBI, + node_output &channel); + void UpdateTimeSource(multi_buffer_info &MBI, + multi_buffer_info &oldMBI, + node_input &input); +#endif +// node_output* FindOutput(media_source source); +// node_input* FindInput(media_destination dest); +// node_input* FindInput(int32 destinationId); + +// void ProcessGroup(BParameterGroup *group, int32 index, int32 &nbParameters); +// void ProcessMux(BDiscreteParameter *parameter, int32 index); + + status_t fInitCheckStatus; + + BMediaAddOn *fAddOn; + int32 fId; + + BList fInputs; + media_input fInput; + + bigtime_t fLatency; + BList fOutputs; + media_output fOutput; + media_format fPreferredFormat; + + bigtime_t fInternalLatency; + // this is computed from the real (negotiated) chunk size and bit rate, + // not the defaults that are in the parameters + bigtime_t fBufferPeriod; + + + //volatile uint32 fBufferCycle; + sem_id fBuffer_free; + + + thread_id fThread; + + BString fHostname; + ESDEndpoint *fDevice; + + //multi_description MD; + //multi_format_info MFI; + //multi_buffer_list MBL; + + //multi_mix_control_info MMCI; + //multi_mix_control MMC[MAX_CONTROLS]; + + bool fTimeSourceStarted; + + BParameterWeb *fWeb; + + BMessage fConfig; +}; + +#endif /* _ESDSINK_NODE_H */ diff --git a/src/add-ons/media/media-add-ons/esound_sink/EsounD-protocol.txt b/src/add-ons/media/media-add-ons/esound_sink/EsounD-protocol.txt new file mode 100644 index 0000000000..dc2def5efd --- /dev/null +++ b/src/add-ons/media/media-add-ons/esound_sink/EsounD-protocol.txt @@ -0,0 +1,279 @@ +EsounD Protocol (Draft) +Author: ymnk +Data: 2000-10-02 +=============== + +Introduction +------------ +This document describes the protocol in EsounD system. +Unfortunately, any formal description about EsounD had not existed. +So the author has tried to read the source code of EsounD and written this +document. The author is also the author of JEsd, which is a re-implementation +of EsounD in pure Java and this document is based on his knowledge, +which had gotten in hacking JEsd. + + +Connection Setup +---------------- +The esd will wait for the TCP connection requests from EsounD compatible +applications. In the default, esd will listen to the TCP port 16001. +The client must send an initial byte of data to be authorized them-self +and to identify the byte order to be employed. +For authorization, client must send 'esd-key', which is a 16 byte data. +For endian-ness, client must send a 4 byte data. +If esd does not detect any error, '1' will be sent back and +'0' will be sent back in error. + +Requests +-------- + lock: + esd-key:ESDKEY + w result:BOOLEAN + + At first, esd will check if the client has the right to lock the device + by esd-key. If that client has the right to do so, esd will lock the device + and send back true. If not, false will be sent back + + unlock: + esd-key:ESDKEY + w result:BOOLEAN + + At first, esd will check if the client has the right to unlock the device + by esd-key. If that client has the right to do so, esd will unlock the + device and send back true. If not, false will be sent back + + ... + + + +Syntactic Conventions +-------------------- +All numbers are in decimal, unless prefixed with '0x', in which case +they are in hexadecimal(base 16). + +The general syntax used to describe data packets is: + Name: + encoded-form + ... + encoded-form + +For components described in the protocol descriptions as: + name: TYPE + w name: TYPE +the encode-form are: + N TYPE name +and +w N TYPE name +N is the number of bytes in the data stream, and TYPE is the interpretation +of those bytes. For example, + result: BOOLEAN +becomes: + 4 BOOLEAN result + +For components with a static numeric value the encode-form is: + N value name +The value is always interpreted as a N-byte unsigned integer. + + +Data Types +---------- + CARD8: A single byte unsigned integer. + CARD32: 32-bit unsigned integer + ARRAY8: A collection of CARD8. + ARRAY8(n): This is a ARRAY8, which includes 'n' elements. + ENDIAN: This is a ARRAY8(4), which includes 'ENDN' or 'NDNE'. + If the first element is 'E', data from clients is in big-endian. + BOOLEAN: This is a CARD32 and includes '0' or '1'. '1' means true. + ESDKEY: This is a ARRAY8(16), which includes 'esd-key'. + ESDNAME: This is a ARRAY8(128), which includes 'esd-name'. + ESDSTREAM: This is a infinite ARRAY8. + FORMAT: This is a CARD32. Each bits in this data has following semantics, + (format&0x000f)==0x0000 8bit data + (format&0x000f)==0x0001 16bit data + (format&0x00f0)==0x0010 mono + (format&0x00f0)==0x0020 stereo + (format&0x0f00)==0x0000 stream + (format&0x0f00)==0x0100 sample + (format&0x0f00)==0x0200 ADPCM + (format&0xf000)==0x1000 play + (format&0xf000)==0x0000 monitor for streams, stop for samples + (format&0xf000)==0x2000 record for streams, loop for samples + MODE: This is a CARD32, which is '0', '1', '2' or '3'. + 0 ERROR + 1 STANDBY + 2 AUTOSTANDBY + 3 RUNNING + + +Packet Format +------------- + init: + 4 0 opcode + 16 ESDKEY esd-key + 4 ENDIAN 'ENDN' or 'NDNE' + w 4 BOOLEAN 0 or 1 + + + lock: + 4 1 opcode + 16 ESDKEY esd-key + 4 ENDIAN unused + w 4 BOOLEAN 0 or 1 + + unlock: + 4 2 opcode + 16 ESDKEY esd-key + 4 ENDIAN unused + w 4 BOOLEAN 0 or 1 + + stream-play: + 4 3 opcode + 4 FORMAT format + 4 CARD32 rate + 128 ESDNAME name + ? ESDSTREAM stream of PCM sound + +//stream-mon: This protocol is not used for the remote esd. +// 4 4 opcode +// 4 FORMA format +// 4 CARD3 rate +// 128 ESDNAME name +// w ? ESDSTREAM stream of PCM sound + + stream-mon: + 4 5 opcode + 4 FORMAT format + 4 CARD32 rate + 128 ESDNAME name + w ? ESDSTREAM stream of PCM sound + + sample-cache: + 4 6 opcode + 4 FORMAT format + 4 CARD32 rate + 4 n size + 128 ESDNAME name + w 4 CARD32 sample-id + n ARRAY8(n) stream of PCM sound + w 4 CARD32 sample-id + + sample-free: + 4 7 opcode + 4 CARD32 sample-id + w 4 CARD32 sample-id + + sample-play: + 4 8 opcode + 4 CARD32 sample-id + w 4 CARD32 sample-id + + sample-loop: + 4 9 opcode + 4 CARD32 sample-id + w 4 CARD32 sample-id + + sample-stop: + 4 10 opcode + 4 CARD32 sample-id + w 4 CARD32 sample-id + + sample-kill: + 4 11 opcode + 4 CARD32 sample-id + w 4 CARD32 sample-id + + standby: + 4 12 opcode + 16 ESDKEY esd-key + 4 ENDIAN unused + w 4 BOOLEAN 0 or 1 + + resume: + 4 13 opcode + 16 ESDKEY esd-key + 4 ENDIAN unused + w 4 BOOLEAN 0 or 1 + + sample-getid: + 4 14 opcode + 128 ESDNAME name + w 4 CARD32 sample-id + + stream-filter: + 4 15 opcode + 4 FORMAT format + 4 CARD32 rate + 128 ESDNAME name + w 4 BOOLEAN 0 or 1 + + server-info: + 4 16 opcode + w 4 CARD32 version + w 4 CARD32 rate + w 4 FORMAT format + + server-all-info: + 4 17 opcode + w 4 CARD32 version + w 4 CARD32 rate + w 4 FORMAT format + w ? STREAMINFO + w ? SAMPLEINFO + +STREAMINFO: + except for last in series + w 4 CARD32 id + w 16 ESDNAME name + w 4 CARD32 rate + w 4 CARD32 left-vol-scale + w 4 CARD32 right-vol-scale + w 4 FORMAT format + last in series + w 4 CARD32 0 + w 32 ARRAY8(32) unused + + +SAMPLEINFO: + except for last in series + w 4 CARD32 id + w 16 ESDNAME name + w 4 CARD32 rate + w 4 CARD32 left-vol-scale + w 4 CARD32 right-vol-scale + w 4 FORMAT format + w 4 CARD32 sample-length + last in series + w 4 CARD32 0 + w 36 ARRAY8(36) unused + + +//subscribe: undefined +// 4 18 opcode + +//unsubjcribe: undefined +// 4 19 opcode + + stream-pan: + 4 20 opcode + 4 CARD32 stream-id + 4 CARD32 left-scale + 4 CARD32 right-scale + w 4 BOOLEAN 0 or 1 + + sample-pan: + 4 21 opcode + 4 CARD32 sample-id + 4 CARD32 left-scale + 4 CARD32 right-scale + w 4 BOOLEAN 0 or 1 + + + standby-mode: + 4 22 opcode + 4 0 version + w 4 MODE mode + w 4 BOOLEAN 0 or 1 + + latency: + 4 23 opcode + w 4 CARD32 latency diff --git a/src/add-ons/media/media-add-ons/esound_sink/Jamfile b/src/add-ons/media/media-add-ons/esound_sink/Jamfile new file mode 100644 index 0000000000..31f250749a --- /dev/null +++ b/src/add-ons/media/media-add-ons/esound_sink/Jamfile @@ -0,0 +1,20 @@ +SubDir HAIKU_TOP src add-ons media media-add-ons esound_sink ; + +SetSubDirSupportedPlatformsBeOSCompatible ; + +if $(TARGET_PLATFORM) != haiku { + SubDirC++Flags -fmultiple-symbol-spaces ; +} + +Addon ESDSink.media_addon : media : + ESDEndpoint.cpp + ESDSinkAddOn.cpp + ESDSinkNode.cpp + : false + : be media +; + +#Package haiku-multi_audio-cvs +# : hmulti_audio.media_addon +# : boot home config add-ons media ; + diff --git a/src/add-ons/media/media-add-ons/esound_sink/compat.h b/src/add-ons/media/media-add-ons/esound_sink/compat.h new file mode 100644 index 0000000000..32ca307ce6 --- /dev/null +++ b/src/add-ons/media/media-add-ons/esound_sink/compat.h @@ -0,0 +1,26 @@ + +#ifndef _COMPAT_H +#define _COMPAT_H + +#include +#include +#include + +#if IPPROTO_TCP != 6 +/* net_server */ + +#else + +# define closesocket close + +# ifdef BONE_VERSION +/* BONE */ + +# else +/* Haiku ? */ + +# endif + +#endif + +#endif /* _COMPAT_H */ diff --git a/src/add-ons/media/media-add-ons/esound_sink/debug.h b/src/add-ons/media/media-add-ons/esound_sink/debug.h new file mode 100644 index 0000000000..835882f2e8 --- /dev/null +++ b/src/add-ons/media/media-add-ons/esound_sink/debug.h @@ -0,0 +1,34 @@ + +#include + +#ifndef NDEBUG + + #ifndef DEBUG + #define DEBUG 2 + #endif + + #if DEBUG >= 1 + #define UNIMPLEMENTED() printf("UNIMPLEMENTED %s\n",__PRETTY_FUNCTION__) + #else + #define UNIMPLEMENTED() ((void)0) + #endif + + #if DEBUG >= 2 + #define BROKEN() printf("BROKEN %s\n",__PRETTY_FUNCTION__) + #else + #define BROKEN() ((void)0) + #endif + + #if DEBUG >= 3 + #define CALLED() printf("CALLED %s\n",__PRETTY_FUNCTION__) + #else + #define CALLED() ((void)0) + #endif + +#else + + #define UNIMPLEMENTED() ((void)0) + #define BROKEN() ((void)0) + #define CALLED() ((void)0) + +#endif diff --git a/src/add-ons/media/media-add-ons/esound_sink/esdproto.h b/src/add-ons/media/media-add-ons/esound_sink/esdproto.h new file mode 100644 index 0000000000..f6c90e8070 --- /dev/null +++ b/src/add-ons/media/media-add-ons/esound_sink/esdproto.h @@ -0,0 +1,131 @@ +/* + * ESounD media addon for BeOS + * + * Copyright (c) 2006 François Revol (revol@free.fr) + * + * Based on Multi Audio addon for Haiku, + * Copyright (c) 2002, 2003 Jerome Duval (jerome.duval@free.fr) + * + * All rights reserved. + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * - Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * - Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ +#ifndef _ESDPROTO_H +#define _ESDPROTO_H +/* + * ESounD protocol + * cf. http://www.jcraft.com/jesd/EsounD-protocol.txt + */ + +#include + +/* limits */ +#define ESD_MAX_BUF (4 * 1024) +#define ESD_MAX_KEY 16 +#define ESD_MAX_NAME 128 + +typedef uint32_t esd_rate_t; +typedef uint32_t esd_format_t; +typedef uint32_t esd_command_t; +typedef char esd_key_t[ESD_MAX_KEY]; +typedef char esd_name_t[ESD_MAX_NAME]; + +/* defaults */ +//#define ESD_PORT 5001 +#define ESD_PORT 16001 + +#define ESD_DEFAULT_RATE 44100 +//#define ESD_DEFAULT_RATE 22050 + +#define ESD_ENDIAN_TAG 'ENDN' + +enum { + /* init */ + ESD_CMD_CONNECT = 0, + + /* security */ + ESD_CMD_LOCK, + ESD_CMD_UNLOCK, + + ESD_CMD_STREAM_PLAY, + ESD_CMD_STREAM_REC, + ESD_CMD_STREAM_MON, + + ESD_CMD_SAMPLE_CACHE, + ESD_CMD_SAMPLE_FREE, + ESD_CMD_SAMPLE_PLAY, + ESD_CMD_SAMPLE_LOOP, + ESD_CMD_SAMPLE_STOP, + ESD_CMD_SAMPLE_KILL, + + ESD_CMD_STANDBY, + ESD_CMD_RESUME, + + ESD_CMD_SAMPLE_GETID, + ESD_CMD_STREAM_FILTER, + + ESD_CMD_SERVER_INFO, + ESD_CMD_SERVER_ALL_INFO, + _ESD_CMD_SUBSCRIBE, + _ESD_CMD_UNSUBSCRIBE, + + ESD_CMD_STREAM_PAN, + ESD_CMD_SAMPLE_PAN, + + ESD_CMD_STANDBY_MODE, + ESD_CMD_LATENCY +}; + +#define ESD_MASK_BITS 0x000F +#define ESD_MASK_CHAN 0x00F0 +#define ESD_MASK_MODE 0x0F00 +#define ESD_MASK_FUNC 0xF000 + +/* sample size */ +#define ESD_BITS8 0x0000 +#define ESD_BITS16 0x0001 + +/* channel count */ +#define ESD_MONO 0x0010 +#define ESD_STEREO 0x0020 + +/* mode */ +#define ESD_STREAM 0x0000 +#define ESD_SAMPLE 0x0100 +#define ESD_ADPCM 0x0200 + +/* functions */ +#define ESD_FUNC_PLAY 0x1000 +#define ESD_FUNC_MONITOR 0x0000 +#define ESD_FUNC_RECORD 0x2000 +#define ESD_FUNC_STOP 0x0000 +#define ESD_FUNC_LOOP 0x2000 + +/* errors */ +#define ESD_OK 0 +#define ESD_ERROR_STANDBY 1 +#define ESD_ERROR_AUTOSTANDBY 2 +#define ESD_ERROR_RUNNING 3 + +#define ESD_FALSE 0 +#define ESD_TRUE 1 + +#endif /* _ESDPROTO_H */ diff --git a/src/add-ons/media/media-add-ons/esound_sink/makefile b/src/add-ons/media/media-add-ons/esound_sink/makefile new file mode 100644 index 0000000000..89e9957e69 --- /dev/null +++ b/src/add-ons/media/media-add-ons/esound_sink/makefile @@ -0,0 +1,128 @@ +## BeOS Generic Makefile v2.2 ## + +## Fill in this file to specify the project being created, and the referenced +## makefile-engine will do all of the hard work for you. This handles both +## Intel and PowerPC builds of the BeOS. + +# check for net_server vs BONE +ifeq ($(findstring headers/be/bone,$(BEINCLUDES)),) +NETLIBS=net +else +NETLIBS=socket bind +endif + +## Application Specific Settings --------------------------------------------- + +# specify the name of the binary +NAME= ESDSink.media_addon + +# specify the type of binary +# APP: Application +# SHARED: Shared library or add-on +# STATIC: Static library archive +# DRIVER: Kernel Driver +TYPE= SHARED + +# add support for new Pe and Eddie features +# to fill in generic makefile + +#%{ +# @src->@ + +# specify the source files to use +# full paths or paths relative to the makefile can be included +# all files, regardless of directory, will have their object +# files created in the common object directory. +# Note that this means this makefile will not work correctly +# if two source files with the same name (source.c or source.cpp) +# are included from different directories. Also note that spaces +# in folder names do not work well with this makefile. +SRCS= $(wildcard *.cpp) + +# specify the resource files to use +# full path or a relative path to the resource file can be used. +RSRCS= + +# @<-src@ +#%} + +# end support for Pe and Eddie + +# specify additional libraries to link against +# there are two acceptable forms of library specifications +# - if your library follows the naming pattern of: +# libXXX.so or libXXX.a you can simply specify XXX +# library: libbe.so entry: be +# +# - if your library does not follow the standard library +# naming scheme you need to specify the path to the library +# and it's name +# library: my_lib.a entry: my_lib.a or path/my_lib.a +LIBS= be media $(NETLIBS) + + +# specify additional paths to directories following the standard +# libXXX.so or libXXX.a naming scheme. You can specify full paths +# or paths relative to the makefile. The paths included may not +# be recursive, so include all of the paths where libraries can +# be found. Directories where source files are found are +# automatically included. +LIBPATHS= + +# additional paths to look for system headers +# thes use the form: #include
+# source file directories are NOT auto-included here +SYSTEM_INCLUDE_PATHS = + +# additional paths to look for local headers +# thes use the form: #include "header" +# source file directories are automatically included +LOCAL_INCLUDE_PATHS = + +# specify the level of optimization that you desire +# NONE, SOME, FULL +OPTIMIZE= + +# specify any preprocessor symbols to be defined. The symbols will not +# have their values set automatically; you must supply the value (if any) +# to use. For example, setting DEFINES to "DEBUG=1" will cause the +# compiler option "-DDEBUG=1" to be used. Setting DEFINES to "DEBUG" +# would pass "-DDEBUG" on the compiler's command line. +DEFINES= + +# specify special warning levels +# if unspecified default warnings will be used +# NONE = supress all warnings +# ALL = enable all warnings +WARNINGS = + +# specify whether image symbols will be created +# so that stack crawls in the debugger are meaningful +# if TRUE symbols will be created +SYMBOLS = + +# specify debug settings +# if TRUE will allow application to be run from a source-level +# debugger. Note that this will disable all optimzation. +DEBUGGER = + +# specify additional compiler flags for all files +COMPILER_FLAGS = + +# specify additional linker flags +LINKER_FLAGS = + +# specify the version of this particular item +# (for example, -app 3 4 0 d 0 -short 340 -long "340 "`echo -n -e '\302\251'`"1999 GNU GPL") +# This may also be specified in a resource. +APP_VERSION = + +# (for TYPE == DRIVER only) Specify desired location of driver in the /dev +# hierarchy. Used by the driverinstall rule. E.g., DRIVER_PATH = video/usb will +# instruct the driverinstall rule to place a symlink to your driver's binary in +# ~/add-ons/kernel/drivers/dev/video/usb, so that your driver will appear at +# /dev/video/usb when loaded. Default is "misc". +DRIVER_PATH = + +## include the makefile-engine +include $(BUILDHOME)/etc/makefile-engine