I don't know how you'd go about doing that with python. I2c is exposed via the i2c-dev driver, so anything that can open files and use ioctls should do and I assume there are plenty of libraries available.
Now, I have to ask what's so important about doing it in python? It you are sampling the inputs at a reasonably high rate, it adds overhead and latency. You could probably write something simple enough in C++ that sends OSC to Supercollider.
Something like this should be a good starting point, and you only need to fill in the sensor-specific stuff.
#include <vector>
#include <string>
#include <unistd.h>
#define OSCPKT_OSTREAM_OUTPUT
#include "oscpkt.hh"
#include <iostream> // needed for udp.hh
#include "udp.hh"
#include <signal.h>
int shouldStop = 0;
void interrupt_handler(int var)
{
shouldStop = true;
}
static oscpkt::UdpSocket gSock;
static int sendOscFloats(const std::string& address, float* values, unsigned int size);
int main(int argc, char** argv)
{
int i2cBus = -1;
gSock.connectTo("localhost", 1234);
signal(SIGINT, interrupt_handler);
std::vector<float> values;
values.resize(numSensors);
// TODO: initialise sensors and multiplexers
while(!shouldStop) {
for(unsigned int n = 0; n < values.size(): ++n) {
float val;
// TODO: read sensor n into val
values[n] = val
}
sendOscFloats("/adc", values.data(), values.size());
usleep(100000); // reduce to increase rate
}
return 0;
}
int sendOsc(const oscpkt::Message& msg)
{
oscpkt::PacketWriter pw;
pw.addMessage(msg);
bool ok = gSock.sendPacket(pw.packetData(), pw.packetSize());
if(!ok) {
fprintf(stderr, "could not send\n");
return -1;
}
return 0;
}
int sendOscFloats(const std::string& address, float* values, unsigned int size)
{
if(!size)
return 0;
oscpkt::Message msg(address);
for(unsigned int n = 0; n < size; ++n)
msg.pushFloat(values[n]);
return sendOsc(msg);
}