aboutsummaryrefslogtreecommitdiff
path: root/src/opcodes.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/opcodes.cpp')
-rw-r--r--src/opcodes.cpp324
1 files changed, 324 insertions, 0 deletions
diff --git a/src/opcodes.cpp b/src/opcodes.cpp
new file mode 100644
index 0000000..c975f42
--- /dev/null
+++ b/src/opcodes.cpp
@@ -0,0 +1,324 @@
+/*
+ opcodes.c
+ Copyright (C) 2025 Richard Knight
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 3 of the License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with this program; if not, write to the Free Software Foundation,
+ Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+
+*/
+#include <plugin.h>
+#include "handling.h"
+#include "shout.h"
+#include "lame/lame.h"
+#include "limits.h"
+
+#define ARGT static constexpr char const*
+#define ARGO ARGT otypes
+#define ARGI ARGT itypes
+
+char* shout_strerror(csnd::Csound* csound, int val) {
+ const char* output;
+ switch (val) {
+ case SHOUTERR_INSANE:
+ output = "bad parameters";
+ break;
+ case SHOUTERR_MALLOC:
+ output = "could not allocate memory";
+ break;
+ case SHOUTERR_NOCONNECT:
+ output = "could not establish connection";
+ break;
+ case SHOUTERR_NOLOGIN:
+ output = "login refused";
+ break;
+ case SHOUTERR_SOCKET:
+ output = "socket error";
+ break;
+ case SHOUTERR_METADATA:
+ output = "metadata update error";
+ break;
+ case SHOUTERR_CONNECTED:
+ output = "connected, but disconnected state required";
+ break;
+ case SHOUTERR_UNCONNECTED:
+ output = "disconnected, but connected state required";
+ break;
+ case SHOUTERR_UNSUPPORTED:
+ output = "unsupported operation";
+ break;
+ }
+ return csound->strdup((char*) output);
+}
+
+const char* handleName = "shout";
+
+struct ShoutSession {
+ shout_t* shout;
+ shout_metadata_t* meta;
+ bool open;
+};
+
+struct shoutinit : csnd::Plugin<1, 5> {
+ ARGO = "i";
+ ARGI = "SiSSS";
+
+ int init() {
+ ShoutSession* session = (ShoutSession*) csound->malloc(sizeof(ShoutSession));
+ outargs[0] = createHandle<ShoutSession>(csound, &session, handleName);
+ STRINGDAT &host = inargs.str_data(0);
+ int port = (int) inargs[1];
+ STRINGDAT &user = inargs.str_data(2);
+ STRINGDAT &password = inargs.str_data(3);
+ STRINGDAT &mount = inargs.str_data(4);
+
+ if (!(session->shout = shout_new())) {
+ return csound->init_error("Could not allocate shout");
+ }
+
+ if (shout_set_host(session->shout, host.data) != SHOUTERR_SUCCESS) {
+ return csound->init_error("Could not set host");
+ }
+
+ if (shout_set_protocol(session->shout, SHOUT_PROTOCOL_HTTP) != SHOUTERR_SUCCESS) {
+ return csound->init_error("Could not set protocol");
+ }
+
+ if (shout_set_port(session->shout, port) != SHOUTERR_SUCCESS) {
+ return csound->init_error("Could not set port");
+ }
+
+ if (shout_set_password(session->shout, password.data) != SHOUTERR_SUCCESS) {
+ return csound->init_error("Could not set password");
+ }
+
+ if (strcmp(mount.data, "") != 0 && shout_set_mount(session->shout, mount.data) != SHOUTERR_SUCCESS) {
+ return csound->init_error("Could not set mount");
+ }
+
+ if (strcmp(user.data, "") != 0 && shout_set_user(session->shout, user.data) != SHOUTERR_SUCCESS) {
+ return csound->init_error("Could not set user");
+ }
+
+ if (shout_set_format(session->shout, SHOUT_FORMAT_MP3) != SHOUTERR_SUCCESS) {
+ return csound->init_error("Could not set format");
+ }
+
+ session->meta = NULL;
+ session->open = false;
+ return OK;
+ }
+};
+
+struct shoutopen : csnd::InPlug<1> {
+ ARGO = "";
+ ARGI = "i";
+ int init() {
+ ShoutSession* session;
+ if (!(session = getHandle<ShoutSession>(csound, args[0], handleName))) {
+ return csound->init_error("Invalid shout session handle");
+ }
+ int res = shout_open(session->shout);
+ if (res != SHOUTERR_SUCCESS) {
+ shout_close(session->shout);
+ char* err = csound->strdup((char*) shout_get_error(session->shout));
+ return csound->init_error(err);
+ }
+ session->meta = NULL;
+ session->open = true;
+ return OK;
+ }
+};
+
+struct shoutclose : csnd::InPlug<1> {
+ ARGO = "";
+ ARGI = "i";
+ int init() {
+ ShoutSession* session;
+ if (!(session = getHandle<ShoutSession>(csound, args[0], handleName))) {
+ return csound->init_error("Invalid shout session handle");
+ }
+ if (session->open) {
+ shout_close(session->shout);
+ session->open = false;
+ }
+ if (session->meta != NULL) {
+ shout_metadata_free(session->meta);
+ session->meta = NULL;
+ }
+ return OK;
+ }
+};
+
+int setdata(csnd::Csound* csound, MYFLT id, int type, char* value) {
+ ShoutSession* session;
+ if (!(session = getHandle<ShoutSession>(csound, id, handleName))) {
+ return csound->init_error("Invalid shout session handle");
+ }
+ shout_t* s = session->shout;
+ int res;
+ switch (type) {
+ case 0:
+ res = shout_set_name(s, value);
+ break;
+ case 1:
+ res = shout_set_public(s, !strcmp(value, "public"));
+ break;
+ case 2:
+ res = shout_set_url(s, value);
+ break;
+ case 3:
+ res = shout_set_genre(s, value);
+ break;
+ case 4:
+ res = shout_set_description(s, value);
+ break;
+ }
+ if (res != SHOUTERR_SUCCESS) {
+ return csound->init_error((char*) shout_get_error(session->shout));
+ } else {
+ return OK;
+ }
+}
+
+#define SHOUTSET(settype)\
+ ARGO = "";\
+ ARGI = "iS";\
+ int init() {\
+ STRINGDAT &value = args.str_data(1);\
+ return setdata(csound, args[0], settype, value.data);\
+ }\
+
+
+struct shoutsetname : csnd::InPlug<2> {
+ SHOUTSET(0)
+};
+
+struct shoutseturl : csnd::InPlug<2> {
+ SHOUTSET(2);
+};
+
+struct shoutsetgenre : csnd::InPlug<2> {
+ SHOUTSET(3);
+};
+
+struct shoutsetdescription : csnd::InPlug<2> {
+ SHOUTSET(4);
+};
+
+struct shoutsetpublic : csnd::InPlug<2> {
+ ARGO = "";
+ ARGI = "ii";
+ int init() {
+ return setdata(csound, args[0], 1, (char*) ((args[1] == 0) ? "private" : "public"));
+ }
+};
+
+
+
+struct shoutsetmeta : csnd::InPlug<3> {
+ ARGO = "";
+ ARGI = "iSS";
+ int init() {
+ ShoutSession* session;
+ if (!(session = getHandle<ShoutSession>(csound, args[0], handleName))) {
+ return csound->init_error("Invalid shout session handle");
+ }
+ if (!session->open) {
+ return csound->init_error("Connection is not open");
+ }
+
+ if (session->meta == NULL) {
+ if (!(session->meta = shout_metadata_new())) {
+ return csound->init_error("Could not allocate metadata");
+ } else {
+ shout_set_metadata(session->shout, session->meta);
+ }
+ }
+
+ STRINGDAT &key = args.str_data(1);
+ STRINGDAT &value = args.str_data(2);
+
+ int mres = shout_metadata_add(session->meta, key.data, value.data);
+ return OK;
+ }
+};
+
+
+struct shoutsend : csnd::InPlug<4> {
+ ARGO = "";
+ ARGI = "iaaj";
+ ShoutSession* session;
+ lame_global_flags* lame;
+ unsigned char* outbuffer;
+ int* inbufferL;
+ int* inbufferR;
+ int inbufferPos;
+ int inbufferSize;
+ int outSize;
+ int outMaxSize;
+
+ int init() {
+ if (!(session = getHandle<ShoutSession>(csound, args[0], handleName))) {
+ return csound->init_error("Invalid shout session handle");
+ }
+ lame = lame_init();
+ if (!lame) {
+ return csound->init_error("Could not setup LAME");
+ }
+ lame_set_num_channels(lame, 2);
+ lame_set_in_samplerate(lame, csound->sr());
+ lame_set_quality(lame, (args[3] == FL(-1)) ? 4 : (int) args[3]);
+ lame_set_out_samplerate(lame, csound->sr());
+ lame_init_params(lame);
+ shout_set_audio_info(session->shout, SHOUT_AI_BITRATE, "128");
+ outSize = 0;
+ inbufferPos = 0;
+ inbufferSize = 2048; // ksmps derived?
+ outMaxSize = inbufferSize; // * 2; //8192;
+ outbuffer = (unsigned char*) csound->malloc(sizeof(unsigned char) * outMaxSize);
+ inbufferL = (int*) csound->malloc(sizeof(int) * inbufferSize);
+ inbufferR = (int*) csound->malloc(sizeof(int) * inbufferSize);
+ return OK;
+ }
+
+ int aperf() {
+ for (int i = 0; i < nsmps; i ++) {
+ inbufferL[inbufferPos] = (int) (args(1)[i] * INT_MAX);
+ inbufferR[inbufferPos ++] = (int) (args(2)[i] * INT_MAX);
+ if (inbufferPos >= inbufferSize) {
+ outSize += lame_encode_buffer_int(lame, inbufferL, inbufferR, inbufferSize, outbuffer, outMaxSize);
+ //std::cout << shout_delay(session->shout) << "\n";
+ //shout_sync(session->shout);
+ shout_send(session->shout, outbuffer, outSize);
+ inbufferPos = 0;
+ outSize = 0;
+ }
+ }
+ return OK;
+ }
+};
+
+#include <modload.h>
+void csnd::on_load(csnd::Csound *csound) {
+ csnd::plugin<shoutinit>(csound, "shoutinit", csnd::thread::i);
+ csnd::plugin<shoutopen>(csound, "shoutopen", csnd::thread::i);
+ csnd::plugin<shoutclose>(csound, "shoutclose", csnd::thread::i);
+ csnd::plugin<shoutsend>(csound, "shoutsend", csnd::thread::ia);
+ csnd::plugin<shoutsetname>(csound, "shoutsetname", csnd::thread::i);
+ csnd::plugin<shoutseturl>(csound, "shoutseturl", csnd::thread::i);
+ csnd::plugin<shoutsetgenre>(csound, "shoutsetgenre", csnd::thread::i);
+ csnd::plugin<shoutsetdescription>(csound, "shoutsetdescription", csnd::thread::i);
+ csnd::plugin<shoutsetpublic>(csound, "shoutsetpublic", csnd::thread::i);
+ csnd::plugin<shoutsetmeta>(csound, "shoutsetmeta", csnd::thread::i);
+}