aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRichard Knight <q@1bpm.net>2021-08-16 03:24:24 +0100
committerRichard Knight <q@1bpm.net>2021-08-16 03:24:24 +0100
commit4f73db87c653ccffcb3cddddaf003b9d09546122 (patch)
tree2d1cf973161efd1d8a116988972cffc5ede2f499
parent2e854bcb40d535957b589ad02e3f280e00b47865 (diff)
downloadcsound-datacore-4f73db87c653ccffcb3cddddaf003b9d09546122.tar.gz
csound-datacore-4f73db87c653ccffcb3cddddaf003b9d09546122.tar.bz2
csound-datacore-4f73db87c653ccffcb3cddddaf003b9d09546122.zip
patch up
-rw-r--r--README.md101
-rw-r--r--src/opcodes.cpp410
2 files changed, 44 insertions, 467 deletions
diff --git a/README.md b/README.md
index f445476..d765943 100644
--- a/README.md
+++ b/README.md
@@ -1,17 +1,16 @@
-# csound-xtract : Csound feature extraction using libXtract
+# csound-memread : Memory audification/sonification for Csound on Linux
## Overview
-csound-xtract is a set of plugin opcodes which use libXtract to perform feature extraction and associated tasks from within Csound.
-
-Development is still ongoing and subject to various research matters, thus is provided in an experimental/alpha state and may contain bugs. Parts of the code are due overhauls and refactoring, but the intention is for the opcodes and general operation to remain the same as presented here.
-
+csound-memread provides a way to read data associated with a process ID as audio.
+Only Linux is supported or an OS that supports userspace reading of /proc/.../mem
## Requirements
-* Cmake >= 3.8
+* Linux
+* Cmake >= 2.8.12
* Csound with development headers >= 6.14.0
-* [LibXtract](https://github.com/jamiebullock/LibXtract)
+* Optional: X11 with development libraries
-Tested on Linux and Windows 7 with MSYS as of March 2021.
+Tested on Linux as of August 2021.
## Installation
@@ -22,7 +21,7 @@ eg:
cmake ..
make && sudo make install
-Cmake should find Csound and libXtract using the modules in the cmake/Modules directory and installation should be as simple as above.
+Cmake should find Csound and X11 using the modules in the cmake/Modules directory and installation should be as simple as above.
## Examples
Some examples are provided in the examples directory.
@@ -30,79 +29,37 @@ Some examples are provided in the examples directory.
## Opcode reference
-### iprofile xtprofile [ibuffersize=4096, iblocksize=512, imfccs=1, icentroid=1, izerocrossings=1, irms=1, iflatness=1, iirregularity=1, ipower=1, isharpness=1, ismoothness=1]
-Declare a feature extraction profile for use in extraction opcodes. The exact techniques for extraction of individual features can be found by examining the libXtract documentation and source code.
-
-* iprofile : the profile handle
-
-* ibuffersize : buffer size used in extraction
-* iblocksize : block size for extraction
-* imfccs : use MFCCs
-* icentroid : use spectral centroid
-* izerocrossings : use zero crossings
-* irms : use RMS
-* iflatness : use spectral flatness
-* iirregularity : use spectral irregularity
-* ipower : use spectral power
-* isharpness : use spectral sharpnesss
-
-
-### icorpus xtcorpus iprofile, ifn
-Analyse sound contained in a f-table and store in a handle, for later use in comparison/matching opcodes. Done during init time.
-
-* icorpus : the corpus handle
-
-* iprofile : profile handle as created by xtprofile
-* ifn : f-table containing the sound to be analysed, typically GEN1
-
-
-### ixtract, kdone xtractor iprofile, ain
-Analyse a live sound.
-
-* ixtract : the extraction handle
-
-* iprofile : profile handle as created by xtprofile
-* ain : sound to analyse
-
-
-### kdistance xtdistance ixtract1, ixtract2, ktrigger, [idistancefunc=0]
-Compare two extraction streams using a basic distance function between each frame of the analyses. The profiles used for the streams must be the same.
-
-* kdistance : the calculated distance, 0 should represent no difference between the analyses.
-
-* ixtract1 : analysis stream as created by xtractor
-* ixtract2 : analysis stream as created by xtractor
-* ktrigger : comparison is conducted when 1
-* idistancefunc : 0 for Euclidean distance, 1 for Manhattan distance
-
-
-### kanalysis[] xtdump ixtract
-Obtain the analysis data from an xtractor handle. The array length will depend on the number of features specified in the profile used (MFCC uses 13 indexes, all others use 1). The indexes are presented in the same order as declared in xtprofile. For example, a profile using MFCC and centroid would mean indexes 0 to 12 would be MFCCs, and 13 would be centroid. Similarly a profile using only centroid and flatness would imply index 0 would be centroid, and 1 would be flatness.
+### ipids[] memps
+Obtain a list of process IDs owned by the executing user.
-* kanalysis[] : the analysed features
+* ipids[] : array of process IDs
-* ixtract : analysis stream as created by xtractor
+### Sname mempsname ipid
+Get the process command line or name for a given process ID if available.
-### kdone, kanalysis[] xtaccdump ixtract, ktrigger
-Obtain the analysis data from an xtractor handle as with xtdump, but accumulate the analyses and output the mean when ktrigger is 1.
+* ipid : the process ID
-* kdone : outputs 1 when new data is provided, 0 at all other times
-* kanalysis[] : the analysed features as with xtdump.
+* Sname : the process command line
-* ixtract : analysis stream as created by xtractor
-* ktrigger : triggers the output of data when 1
+### ifn mem2tab ipid [, iskipzero=0]
+Read the memory associated with a process ID into a new function table which can be
+used by loscil and other such opcodes.
-### kposition xtcorpusmatch icorpus, ixtract, ktrigger, [idistancefunc=0]
-Obtain the nearest match in a corpus created with xtcorpus by comparing a live input stream created by xtractor.
+* ifn : the function table number created
-* kposition : position of nearest corpus region, in sample points
+* ipid : the process ID
+* iskipzero : if non-zero, skip empty memory locations, without this the output may contain a lot of silence depending on the process
-* icorpus : corpus handle as created by xtcorpus
-* ixtract : analysis stream as created by xtractor
-* ktrigger : perform the comparison when 1
-* idistancefunc : 0 for Euclidean distance, 1 for Manhattan distance
+### aout memson ipid, koffset, kbuffermultiplier [, ibuffersize=441000, iskipzero=0]
+Buffered memory reading and direct audio output. The buffer is only refilled when koffset changes.
+* aout : the sonified memory data
+* ipid : the process ID
+* koffset : position to read memory from, normalised to between 0 and 1
+* kbuffermultiplier : buffer read size multiplier between 0 and 1
+* ibuffersize : the size in samples of the buffer to read memory data into
+* iskipzero : if non-zero, skip empty memory locations, without this the output may contain a lot of silence depending on the process \ No newline at end of file
diff --git a/src/opcodes.cpp b/src/opcodes.cpp
index 4e1265c..fa252e2 100644
--- a/src/opcodes.cpp
+++ b/src/opcodes.cpp
@@ -171,17 +171,6 @@ struct winson : csnd::Plugin<1, 1> {
#endif
-class MemLocation {
-public:
- unsigned long start;
- unsigned long length;
- unsigned long* content; // for mmap test..
- MemLocation(unsigned long start, unsigned long length) {
- this->start = start;
- this->length = length;
- }
-};
-
class MemParser {
private:
@@ -190,77 +179,9 @@ private:
int pid;
bool skip_zero;
int buffer_write_position;
- std::vector<MemLocation*> locations;
-
- /*
- void iterate_memory(std::function<void (unsigned long, unsigned long)> func) {
- procmaps_iterator* maps = pmparser_parse(csound, pid);
- procmaps_struct* maps_tmp = NULL;
- while ((maps_tmp = pmparser_next(maps)) != NULL) {
- if (maps_tmp->is_r && maps_tmp->is_w) {
- func((unsigned long) maps_tmp->addr_start, (unsigned long) maps_tmp->length);
- }
- }
- pmparser_free(csound, maps);
- }
- */
-
- bool parsed;
- void parse_memory() {
- locations.clear();
- procmaps_iterator* maps = pmparser_parse(csound, pid);
- procmaps_struct* maps_tmp = NULL;
- while ((maps_tmp = pmparser_next(maps)) != NULL) {
- if (maps_tmp->is_r && maps_tmp->is_w) {
- locations.push_back(
- new MemLocation(
- (unsigned long) maps_tmp->addr_start,
- (unsigned long) maps_tmp->length
- )
- );
- std::cout << (unsigned long) maps_tmp->addr_start << "-" << (unsigned long) maps_tmp->length << "\n";
- }
- }
- pmparser_free(csound, maps);
- parsed = true;
- }
-
- // this idea ends up with more mem locations than expected, whytf? vector malloc not right with csound???
- void iterate_memory_step(std::function<bool (long, MYFLT)> func) {
- if (!parsed) parse_memory();
- long i;
- MYFLT val; int x = 0;
- unsigned long* lbuffer = NULL;
- std::cout << "sz=" << locations.size() << "\n";
- for (auto &location : locations) {
- lbuffer = (unsigned long*) csound->malloc(sizeof(unsigned long) * location->length);
- lseek(fd_mem, location->start, SEEK_SET);
- read(fd_mem, lbuffer, location->length);
- std::cout << (unsigned long) location->start << "-" << (unsigned long) location->length << "\n";
- for (i = 0; i < location->length; i++) {
- val = ((MYFLT) lbuffer[i]) / ULONG_MAX;
- if (!skip_zero || (skip_zero && val > 0)) {
- if (!(func(i, val))) goto complete;
- }
-
- }
-
- if (lbuffer != NULL) {
- csound->free(lbuffer); // causes segfault, WHY???????????
- lbuffer = NULL;
- }
-
- }
- complete:
- if (lbuffer != NULL) {
- csound->free(lbuffer); // causes segfault, WHY???????????
- lbuffer = NULL;
- }
-
- }
// func arguments: index and value ; return false to stop iteration, true to continue
- void Xiterate_memory_step(std::function<bool (long, MYFLT)> func) {
+ void iterate_memory_step(std::function<bool (long, MYFLT)> func) {
MYFLT val;
unsigned long* lbuffer = NULL;
procmaps_iterator* maps = pmparser_parse(csound, pid);
@@ -303,7 +224,6 @@ public:
this->csound = csound;
this->skip_zero = skip_zero;
this->pid = pid;
- parsed = false;
total_size = 0;
buffer = NULL;
buffer_size = 0;
@@ -337,7 +257,6 @@ public:
return total_size;
}
-
int fill_buffer(MYFLT offset) {
if (total_size == 0) {
total_size = get_size();
@@ -401,9 +320,8 @@ struct mem2tab : csnd::Plugin<1, 2> {
}
};
-
// read memory from a given pid as audio
-// aout memson ipid, koffset, kbuffer_readratio, ibuffersize=441000, iskipzero=0
+// aout memson ipid, koffset, kbuffermultiplier, ibuffersize=441000, iskipzero=0
struct memson : csnd::Plugin<1, 5> {
static constexpr char const *otypes = "a";
static constexpr char const *itypes = "ikkjo";
@@ -423,324 +341,29 @@ struct memson : csnd::Plugin<1, 5> {
}
int aperf() {
- for (int i = 0; i < nsmps; i++) {
- outargs(0)[i] = mp->buffer[buffer_read_position];
- if (LIKELY(buffer_read_position < mp->buffer_size*inargs[2])) {
- buffer_read_position++;
- } else {
- buffer_read_position = 0;
- }
- }
- if (inargs[1] != last_offset) {
- last_offset = inargs[1];
- mp->fill_buffer(last_offset);
- }
-
- return OK;
-
- }
-
-
-};
-
-
-
-
-
-typedef struct mmap {
- unsigned long start;
- unsigned long length;
-} _mmap;
-
-
-// read memory from a given pid as audio
-// aout memson2 ipid, koffset, kbufsize [, iunsafe]
-// where kbufsize is 0 to 1 and koffset is 0 to 1
-struct memson2 : csnd::Plugin<1, 4> {
- static constexpr char const *otypes = "a";
- static constexpr char const *itypes = "ikkj";
- int pid;
- int buffer_read_position;
- int buffer_write_position;
- int buffer_size;
- int max_buffer_size;
- unsigned long total_locations;
- MYFLT* buffer;
- MYFLT last_buffer_ratio;
- MYFLT last_offset;
- char* mem_path;
- bool unsafe;
-
- int init() {
- max_buffer_size = 441000;
- buffer_size = (int) (inargs[2] * max_buffer_size);
- last_buffer_ratio = inargs[2];
- last_offset = inargs[1];
- unsafe = (((int)inargs[3]) == 1);
- csound->plugin_deinit(this);
- pid = (int) inargs[0];
- buffer = (MYFLT*) csound->malloc(sizeof(MYFLT) * max_buffer_size);
- mem_path = (char*) csound->malloc(sizeof(char) * 50);
- sprintf(mem_path, "/proc/%d/mem", pid);
-
- buffer_read_position = 0;
- buffer_write_position = 0;
- total_locations = max_locations();
- refill_buffer(inargs[1], true);
-
- return OK;
- }
-
- int deinit() {
- csound->free(mem_path);
- return OK;
- }
-
- unsigned long max_locations() {
- procmaps_iterator* maps = pmparser_parse(csound, pid);
- int fd_mem = open(mem_path, O_RDWR);
- unsigned long len = 0;
- procmaps_struct* maps_tmp = NULL;
- while ((maps_tmp = pmparser_next(maps)) != NULL) {
- if (maps_tmp->is_r && maps_tmp->is_w) {
- len += maps_tmp->length;
- }
+ MYFLT buffer_multiplier = inargs[2];
+ if (UNLIKELY(buffer_multiplier > 1)) {
+ buffer_multiplier = 1;
+ } else if (UNLIKELY(buffer_multiplier < 0)) {
+ buffer_multiplier = 0;
}
- pmparser_free(csound, maps);
- close(fd_mem);
- return len;
- }
-
- int read_mem(int fd_mem, unsigned long start, unsigned long length) {
- length = length; // 4; // ??????????????????????????????????????????????????????
- // lbuffer as unsigned char
- // https://unix.stackexchange.com/questions/6301/how-do-i-read-from-proc-pid-mem-under-linux
- unsigned long* lbuffer = (unsigned long*) csound->malloc(sizeof(unsigned long) * length);
- lseek(fd_mem, start, SEEK_SET);
- read(fd_mem, lbuffer, length);
-
- int i;
- for (i = 0; i < length; i++) {
- //std::cout << "a " << buffer_write_position << "x " << buffer_size << "\n";
- MYFLT val = ((MYFLT)lbuffer[i]) / ULONG_MAX;
- buffer[buffer_write_position] = val;
- if (buffer_write_position + 1 < buffer_size) {
- buffer_write_position++;
- } else {
- return 1;
- }
- }
- return 0;
- }
-
-
- int refill_buffer(MYFLT offset, bool first) {
- buffer_write_position = 0;
- if (offset > 1) offset = 1;
- long offset_position = total_locations * offset;
- if (UNLIKELY(offset_position > total_locations - (buffer_size + 1))) {
- offset_position = total_locations - (buffer_size + 1);
- }
- procmaps_iterator* maps = pmparser_parse(csound, pid);
- if (maps == NULL) {
- //csound->message("cannot open maps");
- return NOTOK; //csound->perf_error("cannot open maps", this->insdshead);
- }
-
- int fd_mem = open(mem_path, O_RDWR);
- if (fd_mem == -1) {
- //csound->message("cannot open memory");
- return NOTOK;
- }
-
- long position = 0;
- unsigned long aof = 0;
- procmaps_struct* maps_tmp = NULL;
- int res = 0;
- while ((maps_tmp = pmparser_next(maps)) != NULL) {
- if (maps_tmp->is_r && maps_tmp->is_w) {
- if (position >= offset_position) {
- aof = position - offset_position; // not quite right, length needs sorting
- res = read_mem(fd_mem, ((unsigned long) maps_tmp->addr_start) + aof, (unsigned long) maps_tmp->length);
- }
- position += maps_tmp->length;
- if (res == 1) {
- break;
- }
- }
- }
- pmparser_free(csound, maps);
- close(fd_mem);
- return OK;
- }
-
- int aperf() {
- if (last_offset != inargs[1] || last_buffer_ratio != inargs[2]) {
- last_offset = inargs[1];
- last_buffer_ratio = inargs[2];
- buffer_size = (int) (inargs[2] * max_buffer_size);
- refill_buffer(inargs[1], false);
- buffer_read_position = 0;
-
- }
for (int i = 0; i < nsmps; i++) {
- outargs(0)[i] = buffer[buffer_read_position];
- if (LIKELY(buffer_read_position + 1 < buffer_size)) {
+ outargs(0)[i] = mp->buffer[buffer_read_position];
+ if (LIKELY(buffer_read_position < mp->buffer_size*buffer_multiplier)) {
buffer_read_position++;
} else {
- refill_buffer(inargs[1], false);
buffer_read_position = 0;
}
}
-
- return OK;
-
- }
-
-
-};
-
-
-
-
-
-// read memory from a given pid as audio
-struct memson3 : csnd::Plugin<1, 3> {
- static constexpr char const *otypes = "a";
- static constexpr char const *itypes = "ikk";
- int pid;
- int buffer_read_position;
- int buffer_write_position;
- int buffer_size;
- int max_buffer_size;
- unsigned long total_locations;
- MYFLT* buffer;
- unsigned char* lbuffer;
- MYFLT last_buffer_ratio;
- MYFLT last_offset;
- char* mem_path;
-
- int init() {
- max_buffer_size = 441000;
- buffer_size = (int) (inargs[2] * max_buffer_size);
- last_buffer_ratio = inargs[2];
- last_offset = inargs[1];
- csound->plugin_deinit(this);
- pid = (int) inargs[0];
- buffer = (MYFLT*) csound->malloc(sizeof(MYFLT) * max_buffer_size);
- lbuffer = (unsigned char*) csound->malloc(sizeof(unsigned char) * max_buffer_size);
- mem_path = (char*) csound->malloc(sizeof(char) * 50);
- sprintf(mem_path, "/proc/%d/mem", pid);
-
- buffer_read_position = 0;
- buffer_write_position = 0;
- total_locations = max_locations();
- refill_buffer(inargs[1]);
-
- return OK;
- }
-
- int deinit() {
- csound->free(mem_path);
- return OK;
- }
-
- unsigned long max_locations() {
- procmaps_iterator* maps = pmparser_parse(csound, pid);
- int fd_mem = open(mem_path, O_RDWR);
- unsigned long len = 0;
- procmaps_struct* maps_tmp = NULL;
- while ((maps_tmp = pmparser_next(maps)) != NULL) {
- if (maps_tmp->is_r && maps_tmp->is_w) {
- len += maps_tmp->length;
- }
- }
- pmparser_free(csound, maps);
- close(fd_mem);
- return len;
- }
-
- int read_mem(int fd_mem, unsigned long start, unsigned long length) {
- length = length; // 4; // ??????????????????????????????????????????????????????
- // lbuffer as unsigned char
- // https://unix.stackexchange.com/questions/6301/how-do-i-read-from-proc-pid-mem-under-linux
- lseek(fd_mem, start, SEEK_SET);
- read(fd_mem, lbuffer, length);
-
- int i;
- for (i = 0; i < length; i++) {
- //std::cout << "a " << buffer_write_position << "x " << buffer_size << "\n";
- MYFLT val = ((MYFLT)lbuffer[i]) / UCHAR_MAX;
- buffer[buffer_write_position] = val;
- if (buffer_write_position + 1 < buffer_size) {
- buffer_write_position++;
- } else {
- return 1;
- }
- }
- return 0;
- }
-
-
- int refill_buffer(MYFLT offset) {
- buffer_write_position = 0;
- if (offset > 1) offset = 1;
- long offset_position = total_locations * offset;
- if (UNLIKELY(offset_position > total_locations - (buffer_size + 1))) {
- offset_position = total_locations - (buffer_size + 1);
- }
- procmaps_iterator* maps = pmparser_parse(csound, pid);
-
- if (maps == NULL) {
- //csound->message("cannot open maps");
- return NOTOK; //csound->perf_error("cannot open maps", this->insdshead);
- }
-
- int fd_mem = open(mem_path, O_RDWR);
- if (fd_mem == -1) {
- //csound->message("cannot open memory");
- return NOTOK;
- }
-
- long position = 0;
- unsigned long aof = 0;
- procmaps_struct* maps_tmp = NULL;
- int res = 0;
- while ((maps_tmp = pmparser_next(maps)) != NULL) {
- if (maps_tmp->is_r && maps_tmp->is_w) {
- if (position >= offset_position) {
- aof = position - offset_position; // not quite right, length needs sorting
- res = read_mem(fd_mem, ((unsigned long) maps_tmp->addr_start) + aof, (unsigned long) maps_tmp->length);
- }
- position += maps_tmp->length;
- if (res == 1) {
- break;
- }
- }
- }
- pmparser_free(csound, maps);
- close(fd_mem);
- return OK;
- }
-
- int aperf() {
- if (last_offset != inargs[1] || last_buffer_ratio != inargs[2]) {
+ if (inargs[1] != last_offset) {
last_offset = inargs[1];
- last_buffer_ratio = inargs[2];
- buffer_size = (int) (inargs[2] * max_buffer_size);
- refill_buffer(inargs[1]);
- buffer_read_position = 0;
-
- }
- for (int i = 0; i < nsmps; i++) {
- outargs(0)[i] = buffer[buffer_read_position];
- if (LIKELY(buffer_read_position + 1 < buffer_size)) {
- buffer_read_position++;
- } else {
- refill_buffer(inargs[1]);
- buffer_read_position = 0;
+ if (UNLIKELY(last_offset > 1)) {
+ last_offset = 1;
+ } else if (UNLIKELY(last_offset < 0)) {
+ last_offset = 0;
}
+ mp->fill_buffer(last_offset);
}
return OK;
@@ -751,13 +374,10 @@ struct memson3 : csnd::Plugin<1, 3> {
};
-
#include <modload.h>
void csnd::on_load(csnd::Csound *csound) {
csnd::plugin<memson>(csound, "memson", csnd::thread::ia);
- csnd::plugin<memson2>(csound, "memson2", csnd::thread::ia);
- csnd::plugin<memson3>(csound, "memson3", csnd::thread::ia);
csnd::plugin<memps>(csound, "memps", csnd::thread::i);
csnd::plugin<mempsname>(csound, "mempsname", csnd::thread::i);
csnd::plugin<mem2tab>(csound, "mem2tab", csnd::thread::i);