Skip to content

Commit 7321d1f

Browse files
Merge remote-tracking branch 'libmapper/main' into 4-import-upstream-changes
2 parents 9619aa1 + 9e070e3 commit 7321d1f

5 files changed

Lines changed: 208 additions & 81 deletions

File tree

CMakeLists.txt

Lines changed: 38 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -5,16 +5,35 @@ get_filename_component(PROJECT ${FILENAME} NAME_WE) #automatically sets project
55
message(STATUS "Project name is ${PROJECT}")
66
project (${PROJECT})
77

8-
find_library(MAPPER_LIB mapper)
9-
if(NOT MAPPER_LIB)
10-
message(FATAL_ERROR "libmapper library not found")
11-
endif()
12-
13-
find_package(Boost 1.73.0 REQUIRED)
14-
if(NOT Boost_FOUND)
15-
message(FATAL_ERROR "boost library not found")
16-
endif()
17-
include_directories(Boost_INCLUDE_DIRS)
8+
if (WIN32)
9+
# EDIT LIBMAPPER_DIR PATH BELOW BEFORE COMPILING FOR WINDOWS
10+
set(LIBMAPPER_DIR "C:/Users/brady/Documents/Github/libmapper")
11+
set(LIBMAPPER_BUILD_DIR "${LIBMAPPER_DIR}/build/Release")
12+
set(LIBLO_DIR "${LIBMAPPER_DIR}/build/liblo/liblo-master")
13+
set(LIBLO_BUILD_DIR "${LIBLO_DIR}/cmake/build/Release")
14+
set(LIBLO_INCLUDES "${LIBLO_DIR}/cmake/build;${LIBLO_DIR}")
15+
16+
set(Liblo_LIB ${LIBLO_BUILD_DIR}/liblo.lib)
17+
set(Libmapper_LIB ${LIBMAPPER_BUILD_DIR}/libmapper.lib)
18+
mark_as_advanced(Liblo_LIB)
19+
mark_as_advanced(Libmapper_LIB)
20+
add_definitions(
21+
-D_WINSOCK_DEPRECATED_NO_WARNINGS
22+
-DHAVE_WINSOCK2_H
23+
-DNODEFAULTLIB
24+
)
25+
include_directories(
26+
"${LIBLO_INCLUDES}"
27+
"${LIBMAPPER_DIR}/include"
28+
)
29+
set(MAPPER_LIB ${Liblo_LIB} ${Libmapper_LIB})
30+
else()
31+
find_library(MAPPER_LIB mapper)
32+
if(NOT MAPPER_LIB)
33+
message(FATAL_ERROR "libmapper library not found")
34+
endif(NOT MAPPER_LIB)
35+
include_directories(/usr/local/include)
36+
endif(WIN32)
1837

1938
if (APPLE)
2039
set(CMAKE_INSTALL_PREFIX "${CMAKE_BINARY_DIR}")
@@ -33,8 +52,6 @@ include_directories(${SC_PATH}/include/plugin_interface)
3352
include_directories(${SC_PATH}/include/common)
3453
include_directories(${SC_PATH}/common)
3554

36-
include_directories(/usr/local/include)
37-
3855
if (CMAKE_SYSTEM_NAME MATCHES "Linux|.*BSD|DragonFly")
3956
set(INSTALL_DESTINATION "lib/SuperCollider/plugins")
4057
if (QUARKS)
@@ -136,18 +153,25 @@ endif()
136153
add_library(${PROJECT} MODULE ${FILENAME})
137154
target_link_libraries(${PROJECT} ${MAPPER_LIB} ${Boost_libraries})
138155

156+
if(WIN32)
157+
set_target_properties(${PROJECT} PROPERTIES LINK_FLAGS "/NODEFAULTLIB:MSVCRTD")
158+
set_target_properties(${PROJECT} PROPERTIES LINK_FLAGS "/INCREMENTAL:NO")
159+
endif(WIN32)
160+
139161
if(SUPERNOVA)
140162
add_library(${PROJECT}_supernova MODULE ${FILENAME})
141163
target_link_libraries(${PROJECT}_supernova ${MAPPER_LIB})
142164
set_property(TARGET ${PROJECT}_supernova
143165
PROPERTY COMPILE_DEFINITIONS SUPERNOVA)
166+
if(NOT WIN32)
144167
install(TARGETS ${PROJECT}_supernova
145168
DESTINATION ${INSTALL_DESTINATION}/${PLUGIN_DIR})
169+
endif(NOT WIN32)
146170
endif()
147171

148-
172+
if(NOT WIN32)
149173
install(TARGETS ${PROJECT}
150174
DESTINATION ${INSTALL_DESTINATION}/${PLUGIN_DIR})
151-
152175
install(DIRECTORY "sc/" DESTINATION "${INSTALL_DESTINATION_DISTRO}"
153176
PATTERN "*")
177+
endif(NOT WIN32)

Mapper.cpp

Lines changed: 90 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,6 @@
2222
#include <mapper/mapper.h>
2323

2424
#include <atomic>
25-
#include <boost/lockfree/queue.hpp>
2625
#include <functional>
2726
#include <queue>
2827
#include <thread>
@@ -34,41 +33,41 @@ static mpr_dev dev;
3433
static std::atomic<bool> isReady;
3534
static std::thread* libmapperThreadHandle;
3635

37-
typedef std::function<void()> Task;
38-
static boost::lockfree::queue<Task*> taskQueue(128);
39-
40-
void libmapperThread() {
41-
// TODO(mb): Add option for specifying device name with Mapper.enable(name)
42-
dev = mpr_dev_new("SuperCollider", 0);
36+
void libmapperThread(char* deviceName) {
37+
dev = mpr_dev_new(deviceName, 0);
4338
while (!mpr_dev_get_is_ready(dev)) {
4439
mpr_dev_poll(dev, 10);
4540
}
4641
isReady = true;
4742
Print("Mapper: libmapper ready!\n");
4843
while (dev) {
4944
mpr_dev_poll(dev, 1);
50-
// Execute tasks
51-
Task* f;
52-
if (taskQueue.pop(f)) {
53-
(*f)();
54-
delete f;
55-
}
5645
}
5746
}
5847

59-
struct MapperUnit : public Unit {
48+
struct MapperSignalUnit : public Unit {
6049
int signalNameSize;
6150
char* signalName;
62-
mpr_sig sig;
51+
mpr_sig sig = nullptr;
6352
float sigMin = 0;
6453
float sigMax = 1;
6554
float val = 0;
6655
};
6756

68-
struct MapIn : public MapperUnit {};
69-
struct MapOut : public MapperUnit {};
70-
struct MapperEnabler : public Unit {};
57+
struct MapperDeviceUnit : public Unit {
58+
int deviceNameSize;
59+
char* deviceName;
60+
};
61+
62+
struct MapperIsReadyUnit : public Unit {
63+
bool shouldSendNeg = true; // Send -1 the first frame to trigger free when ready
64+
};
65+
66+
struct MapIn : public MapperSignalUnit {};
67+
struct MapOut : public MapperSignalUnit {};
68+
struct MapperEnabler : public MapperDeviceUnit {};
7169
struct MapperDisabler : public Unit {};
70+
struct MapperIsReady : public MapperIsReadyUnit {};
7271

7372
// Empty DSP function
7473
static void Unit_next_nop(Unit* unit, int inNumSamples) {}
@@ -84,36 +83,24 @@ static void MapOut_next(MapOut* unit, int inNumSamples);
8483
static void MapperEnabler_Ctor(MapperEnabler* unit);
8584
static void MapperDisabler_Ctor(MapperDisabler* unit);
8685

87-
static void MapIn_signalUpdate(mpr_sig sig, mpr_sig_evt evt, mpr_id inst,
88-
int length, mpr_type type, const void* value,
89-
mpr_time time) {
90-
MapIn* m = (MapIn*)mpr_obj_get_prop_as_ptr(sig, MPR_PROP_DATA, 0);
91-
if (m) {
92-
m->val = *reinterpret_cast<const float*>(value);
93-
}
94-
}
86+
static void MapperIsReady_Ctor(MapperIsReady* unit);
87+
static void MapperIsReady_Dtor(MapperIsReady* unit);
88+
static void MapperIsReady_next(MapperIsReady* unit, int inNumSamples);
9589

96-
static void MapperUnit_bindToSignal(MapperUnit* unit, mpr_dir direction) {
90+
static void MapperSignalUnit_bindToSignal(MapperSignalUnit* unit, mpr_dir direction) {
9791
// Search for existing output signal with same name
9892
mpr_list sigs = mpr_dev_get_sigs(dev, direction);
9993
sigs = mpr_list_filter(sigs, MPR_PROP_NAME, 0, 1, MPR_STR, unit->signalName,
10094
MPR_OP_EQ);
10195

102-
mpr_sig_handler* handler = direction == MPR_DIR_IN ? MapIn_signalUpdate : 0;
103-
int flags = direction == MPR_DIR_IN ? MPR_SIG_UPDATE : 0;
104-
10596
if (sigs) {
10697
// Signal exists, bind unit to signal
10798
unit->sig = *sigs;
10899

109-
// Update pointer for signal update callback
110-
mpr_obj_set_prop(unit->sig, MPR_PROP_DATA, 0, 1, MPR_PTR, unit, 0);
111-
mpr_sig_set_cb(unit->sig, handler, flags);
112-
113100
// Update signal metadata if signal range has changed
114-
// mpr_obj_set_prop(unit->sig, MPR_PROP_MIN, 0, 1, MPR_FLT, &unit->sigMin,
115-
// 1); mpr_obj_set_prop(unit->sig, MPR_PROP_MAX, 0, 1, MPR_FLT,
116-
// &unit->sigMax, 1);
101+
mpr_obj_set_prop(unit->sig, MPR_PROP_MIN, 0, 1, MPR_FLT, &unit->sigMin, 1);
102+
mpr_obj_set_prop(unit->sig, MPR_PROP_MAX, 0, 1, MPR_FLT, &unit->sigMax, 1);
103+
mpr_obj_push(unit->sig);
117104

118105
// Update maps containing signal
119106
// mpr_list maps = mpr_sig_get_maps(unit->sig, MPR_DIR_ANY);
@@ -135,10 +122,9 @@ static void MapperUnit_bindToSignal(MapperUnit* unit, mpr_dir direction) {
135122
// }
136123
} else {
137124
// Signal doesn't exist, create new
138-
Print("Creating signal '%s'\n", unit->signalName);
125+
Print("Mapper: Creating signal '%s'\n", unit->signalName);
139126
unit->sig = mpr_sig_new(dev, direction, unit->signalName, 1, MPR_FLT, 0,
140-
&unit->sigMin, &unit->sigMax, 0, handler, flags);
141-
mpr_obj_set_prop(unit->sig, MPR_PROP_DATA, 0, 1, MPR_PTR, unit, 0);
127+
&unit->sigMin, &unit->sigMax, 0, 0, 0);
142128
}
143129
}
144130

@@ -151,6 +137,7 @@ void MapIn_Ctor(MapIn* unit) {
151137
unit->sigMin = IN0(0);
152138
unit->sigMax = IN0(1);
153139

140+
// Set signal name
154141
unit->signalNameSize = IN0(2);
155142
const int signalNameAllocSize = (unit->signalNameSize + 1) * sizeof(char);
156143

@@ -167,9 +154,9 @@ void MapIn_Ctor(MapIn* unit) {
167154
}
168155
unit->signalName[unit->signalNameSize] = 0;
169156

157+
// Bind to signal
170158
if (dev) {
171-
taskQueue.push(
172-
new Task([unit]() { MapperUnit_bindToSignal(unit, MPR_DIR_IN); }));
159+
MapperSignalUnit_bindToSignal(unit, MPR_DIR_IN);
173160
} else {
174161
Print("MapIn: libmapper not enabled\n");
175162
}
@@ -181,7 +168,23 @@ void MapIn_Dtor(MapIn* unit) { RTFree(unit->mWorld, unit->signalName); }
181168

182169
void MapIn_next(MapIn* unit, int inNumSamples) {
183170
float* out = OUT(0);
184-
*out = unit->val;
171+
172+
// Signal is not created yet
173+
if (!unit->sig) {
174+
*out = 0.f;
175+
}
176+
// Get signal value pointer
177+
const float* val =
178+
static_cast<const float*>(mpr_sig_get_value(unit->sig, 0, 0));
179+
180+
// Signal doesn't have a value yet
181+
if (!val) {
182+
*out = 0.f;
183+
return;
184+
}
185+
186+
// Set out value to signal value
187+
*out = *val;
185188
}
186189

187190
// MapOut
@@ -190,6 +193,7 @@ void MapOut_Ctor(MapOut* unit) {
190193
unit->sigMin = IN0(1);
191194
unit->sigMax = IN0(2);
192195

196+
// Set signal name
193197
unit->signalNameSize = IN0(3);
194198
const int signalNameAllocSize = (unit->signalNameSize + 1) * sizeof(char);
195199

@@ -208,8 +212,7 @@ void MapOut_Ctor(MapOut* unit) {
208212
unit->signalName[unit->signalNameSize] = 0;
209213

210214
if (isReady) {
211-
taskQueue.push(
212-
new Task([unit]() { MapperUnit_bindToSignal(unit, MPR_DIR_OUT); }));
215+
MapperSignalUnit_bindToSignal(unit, MPR_DIR_OUT);
213216
} else {
214217
Print("MapOut: libmapper not enabled\n");
215218
SETCALC(Unit_next_nop);
@@ -222,17 +225,37 @@ void MapOut_Ctor(MapOut* unit) {
222225
void MapOut_Dtor(MapOut* unit) { RTFree(unit->mWorld, unit->signalName); }
223226

224227
void MapOut_next(MapOut* unit, int inNumSamples) {
228+
// Signal is not ready yet
229+
if (!unit->sig) {
230+
return;
231+
}
232+
233+
// Set output signal value
225234
float val = IN0(0);
226-
taskQueue.push(
227-
new Task([=]() { mpr_sig_set_value(unit->sig, 0, 1, MPR_FLT, &val); }));
235+
mpr_sig_set_value(unit->sig, 0, 1, MPR_FLT, &val);
228236
}
229237

230238
// MapperEnabler
231239

232240
void MapperEnabler_Ctor(MapperEnabler* unit) {
233241
if (!dev) {
234-
// dev = mpr_dev_new("SuperCollider", 0);
235-
libmapperThreadHandle = new std::thread(libmapperThread);
242+
// Set device name
243+
unit->deviceNameSize = IN0(0);
244+
const int deviceNameAllocSize = (unit->deviceNameSize + 1) * sizeof(char);
245+
246+
void* chunk = RTAlloc(unit->mWorld, deviceNameAllocSize);
247+
if (!chunk) {
248+
Print("MapOut: RT memory allocation failed\n");
249+
SETCALC(Unit_next_nop);
250+
return;
251+
}
252+
253+
unit->deviceName = reinterpret_cast<char*>(chunk);
254+
for (int i = 0; i < unit->deviceNameSize; i++) {
255+
unit->deviceName[i] = static_cast<char>(IN0(1 + i));
256+
}
257+
unit->deviceName[unit->deviceNameSize] = 0;
258+
libmapperThreadHandle = new std::thread(libmapperThread, unit->deviceName);
236259
} else {
237260
Print("Mapper: libmapper already enabled.\n");
238261
}
@@ -250,10 +273,28 @@ void MapperDisabler_Ctor(MapperDisabler* unit) {
250273
SETCALC(Unit_next_nop);
251274
}
252275

276+
// MapperIsReady
277+
278+
void MapperIsReady_Ctor(MapperIsReady* unit) {
279+
SETCALC(MapperIsReady_next);
280+
MapperIsReady_next(unit, 1);
281+
}
282+
void MapperIsReady_Dtor(MapperIsReady* unit) {}
283+
void MapperIsReady_next(MapperIsReady* unit, int inNumSamples) {
284+
float* out = OUT(0);
285+
if (unit->shouldSendNeg) {
286+
*out = -1.0f;
287+
unit->shouldSendNeg = false; // Sent -1 once, send regular value now
288+
} else {
289+
*out = (isReady) ? 1.0f : -1.0f;
290+
}
291+
}
292+
253293
PluginLoad(MapperUGens) {
254294
ft = inTable;
255295
DefineDtorUnit(MapIn);
256296
DefineDtorUnit(MapOut);
257297
DefineSimpleUnit(MapperEnabler);
258298
DefineSimpleUnit(MapperDisabler);
299+
DefineDtorUnit(MapperIsReady);
259300
}

README.md

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,10 +23,40 @@ Please follow the [libmapper](https://github.com/libmapper/libmapper) documentat
2323
2424
## Compilation from source
2525
26+
### GNU/Linux or macOS
27+
2628
```
2729
git clone --recursive https://github.com/IDMIL/MapperUGen.git
2830
cd MapperUGen
2931
mkdir -p build
3032
cmake -B ./build -DSUPERNOVA=ON
3133
cmake --build build --target install
3234
```
35+
36+
### Windows
37+
38+
Note: when cloning supercollider below, if the cloning hangs just execute `git config --global url."https://".insteadOf git://` to fix the issue. It's a known issue with older versions of the supercollider SDK.
39+
40+
```
41+
git clone https://github.com/libmapper/MapperUGen.git
42+
cd MapperUGen
43+
git config --global url."https://".insteadOf git://
44+
git submodule update --init --recursive
45+
```
46+
47+
Now edit the CMakeLists.txt LIBMAPPER_DIR and LIBLO_DIR paths to match with your local libmapper paths. For libmapper installation help, consult the instructions [here](https://github.com/libmapper/libmapper/blob/main/doc/how_to_compile_and_run.md). Finally:
48+
49+
```
50+
mkdir build
51+
cd build
52+
cmake -DSUPERNOVA=ON ..
53+
cmake --build .
54+
```
55+
56+
#### Windows manual installation after compiling
57+
58+
1. Evaluate `Platform.userExtensionDir` in supercollider and create a "Mapper" folder there
59+
2. Copy everything inside the ./sc folder to the Mapper folder
60+
3. Create a "plugins" folder in the Mapper directory
61+
4. Copy the build outputs from ./build/Debug and your libmapper, liblo and zlib .dlls to the plugins folder
62+
5. Reboot the supercollider interpreter

0 commit comments

Comments
 (0)