| @@ -37,7 +37,7 @@ extern "C" | |||||
| JackFloatEncoder = 0, | JackFloatEncoder = 0, | ||||
| JackIntEncoder = 1, | JackIntEncoder = 1, | ||||
| JackCeltEncoder = 2, | JackCeltEncoder = 2, | ||||
| JackMaxEncoder = 3 | |||||
| JackOpusEncoder = 3 | |||||
| }; | }; | ||||
| typedef struct { | typedef struct { | ||||
| @@ -551,6 +551,11 @@ struct JackNetExtSlave : public JackNetSlaveInterface, public JackRunnableInterf | |||||
| return -1; | return -1; | ||||
| } | } | ||||
| if ((fParams.fSampleEncoder == JackOpusEncoder) && (fParams.fKBps == 0)) { | |||||
| jack_error("Opus encoder with 0 for kps..."); | |||||
| return -1; | |||||
| } | |||||
| // Check latency | // Check latency | ||||
| if (fParams.fNetworkLatency > NETWORK_MAX_LATENCY) { | if (fParams.fNetworkLatency > NETWORK_MAX_LATENCY) { | ||||
| jack_error("Error : network latency is limited to %d", NETWORK_MAX_LATENCY); | jack_error("Error : network latency is limited to %d", NETWORK_MAX_LATENCY); | ||||
| @@ -39,6 +39,11 @@ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. | |||||
| #include <celt/celt.h> | #include <celt/celt.h> | ||||
| #endif | #endif | ||||
| #if HAVE_OPUS | |||||
| #include <opus/opus.h> | |||||
| #include <opus/opus_custom.h> | |||||
| #endif | |||||
| #define MIN(x,y) ((x)<(y) ? (x) : (y)) | #define MIN(x,y) ((x)<(y) ? (x) : (y)) | ||||
| using namespace std; | using namespace std; | ||||
| @@ -146,6 +151,13 @@ int JackNetOneDriver::AllocPorts() | |||||
| #endif | #endif | ||||
| celt_mode_info(celt_mode, CELT_GET_LOOKAHEAD, &lookahead); | celt_mode_info(celt_mode, CELT_GET_LOOKAHEAD, &lookahead); | ||||
| netj.codec_latency = 2 * lookahead; | netj.codec_latency = 2 * lookahead; | ||||
| #endif | |||||
| } else if (netj.bitdepth == OPUS_MODE) { | |||||
| #if HAVE_OPUS | |||||
| OpusCustomMode *opus_mode = opus_custom_mode_create(netj.sample_rate, netj.period_size, NULL); // XXX free me in the end | |||||
| OpusCustomDecoder *decoder = opus_custom_decoder_create( opus_mode, 1, NULL ); | |||||
| opus_custom_decoder_init(decoder, opus_mode, 1); | |||||
| netj.capture_srcs = jack_slist_append(netj.capture_srcs, decoder); | |||||
| #endif | #endif | ||||
| } else { | } else { | ||||
| #if HAVE_SAMPLERATE | #if HAVE_SAMPLERATE | ||||
| @@ -191,6 +203,22 @@ int JackNetOneDriver::AllocPorts() | |||||
| CELTMode *celt_mode = celt_mode_create(netj.sample_rate, 1, netj.period_size, NULL); | CELTMode *celt_mode = celt_mode_create(netj.sample_rate, 1, netj.period_size, NULL); | ||||
| netj.playback_srcs = jack_slist_append(netj.playback_srcs, celt_encoder_create(celt_mode)); | netj.playback_srcs = jack_slist_append(netj.playback_srcs, celt_encoder_create(celt_mode)); | ||||
| #endif | #endif | ||||
| #endif | |||||
| } else if (netj.bitdepth == OPUS_MODE) { | |||||
| #if HAVE_OPUS | |||||
| const int kbps = netj.resample_factor; | |||||
| jack_error("NEW ONE OPUS ENCODER 128 <> %d!!", kbps); | |||||
| int err; | |||||
| OpusCustomMode *opus_mode = opus_custom_mode_create( netj.sample_rate, netj.period_size, &err ); // XXX free me in the end | |||||
| if (err != OPUS_OK) { jack_error("opus mode failed"); } | |||||
| OpusCustomEncoder *oe = opus_custom_encoder_create( opus_mode, 1, &err ); | |||||
| if (err != OPUS_OK) { jack_error("opus mode failed"); } | |||||
| opus_custom_encoder_ctl(oe, OPUS_SET_BITRATE(kbps*1024)); // bits per second | |||||
| opus_custom_encoder_ctl(oe, OPUS_SET_COMPLEXITY(10)); | |||||
| opus_custom_encoder_ctl(oe, OPUS_SET_SIGNAL(OPUS_SIGNAL_MUSIC)); | |||||
| opus_custom_encoder_ctl(oe, OPUS_SET_SIGNAL(OPUS_APPLICATION_RESTRICTED_LOWDELAY)); | |||||
| opus_custom_encoder_init(oe, opus_mode, 1); | |||||
| netj.playback_srcs = jack_slist_append(netj.playback_srcs, oe); | |||||
| #endif | #endif | ||||
| } else { | } else { | ||||
| #if HAVE_SAMPLERATE | #if HAVE_SAMPLERATE | ||||
| @@ -450,6 +478,28 @@ JackNetOneDriver::FreePorts () | |||||
| celt_decoder_destroy(dec); | celt_decoder_destroy(dec); | ||||
| } | } | ||||
| netj.capture_srcs = NULL; | netj.capture_srcs = NULL; | ||||
| #endif | |||||
| } else if (netj.bitdepth == OPUS_MODE) { | |||||
| #if HAVE_OPUS | |||||
| node = netj.playback_srcs; | |||||
| while (node != NULL) { | |||||
| JSList *this_node = node; | |||||
| OpusCustomEncoder *enc = (OpusCustomEncoder *) node->data; | |||||
| node = jack_slist_remove_link(node, this_node); | |||||
| jack_slist_free_1(this_node); | |||||
| opus_custom_encoder_destroy(enc); | |||||
| } | |||||
| netj.playback_srcs = NULL; | |||||
| node = netj.capture_srcs; | |||||
| while (node != NULL) { | |||||
| JSList *this_node = node; | |||||
| OpusCustomDecoder *dec = (OpusCustomDecoder *) node->data; | |||||
| node = jack_slist_remove_link(node, this_node); | |||||
| jack_slist_free_1(this_node); | |||||
| opus_custom_decoder_destroy(dec); | |||||
| } | |||||
| netj.capture_srcs = NULL; | |||||
| #endif | #endif | ||||
| } else { | } else { | ||||
| #if HAVE_SAMPLERATE | #if HAVE_SAMPLERATE | ||||
| @@ -724,6 +774,98 @@ JackNetOneDriver::render_jack_ports_to_payload_celt (JSList *playback_ports, JSL | |||||
| } | } | ||||
| #endif | #endif | ||||
| #if HAVE_OPUS | |||||
| #define CDO (sizeof(short)) ///< compressed data offset (first 2 bytes are length) | |||||
| // render functions for Opus. | |||||
| void | |||||
| JackNetOneDriver::render_payload_to_jack_ports_opus (void *packet_payload, jack_nframes_t net_period_down, JSList *capture_ports, JSList *capture_srcs, jack_nframes_t nframes) | |||||
| { | |||||
| int chn = 0; | |||||
| JSList *node = capture_ports; | |||||
| JSList *src_node = capture_srcs; | |||||
| unsigned char *packet_bufX = (unsigned char *)packet_payload; | |||||
| while (node != NULL) { | |||||
| jack_port_id_t port_index = (jack_port_id_t) (intptr_t)node->data; | |||||
| JackPort *port = fGraphManager->GetPort(port_index); | |||||
| jack_default_audio_sample_t* buf = | |||||
| (jack_default_audio_sample_t*)fGraphManager->GetBuffer(port_index, fEngineControl->fBufferSize); | |||||
| const char *portname = port->GetType(); | |||||
| if (strncmp(portname, JACK_DEFAULT_AUDIO_TYPE, jack_port_type_size()) == 0) { | |||||
| // audio port, decode opus data. | |||||
| OpusCustomDecoder *decoder = (OpusCustomDecoder*) src_node->data; | |||||
| if( !packet_payload ) | |||||
| memset(buf, 0, nframes * sizeof(float)); | |||||
| else { | |||||
| unsigned short len; | |||||
| memcpy(&len, packet_bufX, CDO); | |||||
| len = ntohs(len); | |||||
| opus_custom_decode_float( decoder, packet_bufX + CDO, len, buf, nframes ); | |||||
| } | |||||
| src_node = jack_slist_next (src_node); | |||||
| } else if (strncmp(portname, JACK_DEFAULT_MIDI_TYPE, jack_port_type_size()) == 0) { | |||||
| // midi port, decode midi events | |||||
| // convert the data buffer to a standard format (uint32_t based) | |||||
| unsigned int buffer_size_uint32 = net_period_down / 2; | |||||
| uint32_t * buffer_uint32 = (uint32_t*) packet_bufX; | |||||
| if( packet_payload ) | |||||
| decode_midi_buffer (buffer_uint32, buffer_size_uint32, buf); | |||||
| } | |||||
| packet_bufX = (packet_bufX + net_period_down); | |||||
| node = jack_slist_next (node); | |||||
| chn++; | |||||
| } | |||||
| } | |||||
| void | |||||
| JackNetOneDriver::render_jack_ports_to_payload_opus (JSList *playback_ports, JSList *playback_srcs, jack_nframes_t nframes, void *packet_payload, jack_nframes_t net_period_up) | |||||
| { | |||||
| int chn = 0; | |||||
| JSList *node = playback_ports; | |||||
| JSList *src_node = playback_srcs; | |||||
| unsigned char *packet_bufX = (unsigned char *)packet_payload; | |||||
| while (node != NULL) { | |||||
| jack_port_id_t port_index = (jack_port_id_t) (intptr_t) node->data; | |||||
| JackPort *port = fGraphManager->GetPort(port_index); | |||||
| jack_default_audio_sample_t* buf = | |||||
| (jack_default_audio_sample_t*)fGraphManager->GetBuffer(port_index, fEngineControl->fBufferSize); | |||||
| const char *portname = port->GetType(); | |||||
| if (strncmp (portname, JACK_DEFAULT_AUDIO_TYPE, jack_port_type_size()) == 0) { | |||||
| // audio port, encode opus data. | |||||
| int encoded_bytes; | |||||
| jack_default_audio_sample_t *floatbuf = (jack_default_audio_sample_t *)alloca (sizeof(jack_default_audio_sample_t) * nframes); | |||||
| memcpy(floatbuf, buf, nframes * sizeof(jack_default_audio_sample_t)); | |||||
| OpusCustomEncoder *encoder = (OpusCustomEncoder*) src_node->data; | |||||
| encoded_bytes = opus_custom_encode_float( encoder, floatbuf, nframes, packet_bufX + CDO, net_period_up - CDO ); | |||||
| unsigned short len = htons(encoded_bytes); | |||||
| memcpy(packet_bufX, &len, CDO); | |||||
| src_node = jack_slist_next( src_node ); | |||||
| } else if (strncmp(portname, JACK_DEFAULT_MIDI_TYPE, jack_port_type_size()) == 0) { | |||||
| // encode midi events from port to packet | |||||
| // convert the data buffer to a standard format (uint32_t based) | |||||
| unsigned int buffer_size_uint32 = net_period_up / 2; | |||||
| uint32_t * buffer_uint32 = (uint32_t*) packet_bufX; | |||||
| encode_midi_buffer (buffer_uint32, buffer_size_uint32, buf); | |||||
| } | |||||
| packet_bufX = (packet_bufX + net_period_up); | |||||
| node = jack_slist_next (node); | |||||
| chn++; | |||||
| } | |||||
| } | |||||
| #endif | |||||
| /* Wrapper functions with bitdepth argument... */ | /* Wrapper functions with bitdepth argument... */ | ||||
| void | void | ||||
| JackNetOneDriver::render_payload_to_jack_ports (int bitdepth, void *packet_payload, jack_nframes_t net_period_down, JSList *capture_ports, JSList *capture_srcs, jack_nframes_t nframes, int dont_htonl_floats) | JackNetOneDriver::render_payload_to_jack_ports (int bitdepth, void *packet_payload, jack_nframes_t net_period_down, JSList *capture_ports, JSList *capture_srcs, jack_nframes_t nframes, int dont_htonl_floats) | ||||
| @@ -732,6 +874,11 @@ JackNetOneDriver::render_payload_to_jack_ports (int bitdepth, void *packet_paylo | |||||
| if (bitdepth == CELT_MODE) | if (bitdepth == CELT_MODE) | ||||
| render_payload_to_jack_ports_celt (packet_payload, net_period_down, capture_ports, capture_srcs, nframes); | render_payload_to_jack_ports_celt (packet_payload, net_period_down, capture_ports, capture_srcs, nframes); | ||||
| else | else | ||||
| #endif | |||||
| #if HAVE_OPUS | |||||
| if (bitdepth == OPUS_MODE) | |||||
| render_payload_to_jack_ports_opus (packet_payload, net_period_down, capture_ports, capture_srcs, nframes); | |||||
| else | |||||
| #endif | #endif | ||||
| render_payload_to_jack_ports_float (packet_payload, net_period_down, capture_ports, capture_srcs, nframes, dont_htonl_floats); | render_payload_to_jack_ports_float (packet_payload, net_period_down, capture_ports, capture_srcs, nframes, dont_htonl_floats); | ||||
| } | } | ||||
| @@ -743,6 +890,11 @@ JackNetOneDriver::render_jack_ports_to_payload (int bitdepth, JSList *playback_p | |||||
| if (bitdepth == CELT_MODE) | if (bitdepth == CELT_MODE) | ||||
| render_jack_ports_to_payload_celt (playback_ports, playback_srcs, nframes, packet_payload, net_period_up); | render_jack_ports_to_payload_celt (playback_ports, playback_srcs, nframes, packet_payload, net_period_up); | ||||
| else | else | ||||
| #endif | |||||
| #if HAVE_OPUS | |||||
| if (bitdepth == OPUS_MODE) | |||||
| render_jack_ports_to_payload_opus (playback_ports, playback_srcs, nframes, packet_payload, net_period_up); | |||||
| else | |||||
| #endif | #endif | ||||
| render_jack_ports_to_payload_float (playback_ports, playback_srcs, nframes, packet_payload, net_period_up, dont_htonl_floats); | render_jack_ports_to_payload_float (playback_ports, playback_srcs, nframes, packet_payload, net_period_up, dont_htonl_floats); | ||||
| } | } | ||||
| @@ -791,6 +943,10 @@ extern "C" | |||||
| #if HAVE_CELT | #if HAVE_CELT | ||||
| value.ui = 0U; | value.ui = 0U; | ||||
| jack_driver_descriptor_add_parameter(desc, &filler, "celt", 'c', JackDriverParamUInt, &value, NULL, "Set CELT encoding and number of kbits per channel", NULL); | jack_driver_descriptor_add_parameter(desc, &filler, "celt", 'c', JackDriverParamUInt, &value, NULL, "Set CELT encoding and number of kbits per channel", NULL); | ||||
| #endif | |||||
| #if HAVE_OPUS | |||||
| value.ui = 0U; | |||||
| jack_driver_descriptor_add_parameter(desc, &filler, "opus", 'P', JackDriverParamUInt, &value, NULL, "Set Opus encoding and number of kbits per channel", NULL); | |||||
| #endif | #endif | ||||
| value.ui = 0U; | value.ui = 0U; | ||||
| jack_driver_descriptor_add_parameter(desc, &filler, "bit-depth", 'b', JackDriverParamUInt, &value, NULL, "Sample bit-depth (0 for float, 8 for 8bit and 16 for 16bit)", NULL); | jack_driver_descriptor_add_parameter(desc, &filler, "bit-depth", 'b', JackDriverParamUInt, &value, NULL, "Sample bit-depth (0 for float, 8 for 8bit and 16 for 16bit)", NULL); | ||||
| @@ -904,6 +1060,17 @@ extern "C" | |||||
| #endif | #endif | ||||
| break; | break; | ||||
| case 'P': | |||||
| #if HAVE_OPUS | |||||
| bitdepth = OPUS_MODE; | |||||
| resample_factor = param->value.ui; | |||||
| jack_error("OPUS: %d\n", resample_factor); | |||||
| #else | |||||
| jack_error("not built with Opus support"); | |||||
| return NULL; | |||||
| #endif | |||||
| break; | |||||
| case 't': | case 't': | ||||
| handle_transport_sync = param->value.ui; | handle_transport_sync = param->value.ui; | ||||
| break; | break; | ||||
| @@ -45,6 +45,12 @@ class JackNetOneDriver : public JackWaiterDriver | |||||
| render_payload_to_jack_ports_celt(void *packet_payload, jack_nframes_t net_period_down, JSList *capture_ports, JSList *capture_srcs, jack_nframes_t nframes); | render_payload_to_jack_ports_celt(void *packet_payload, jack_nframes_t net_period_down, JSList *capture_ports, JSList *capture_srcs, jack_nframes_t nframes); | ||||
| void | void | ||||
| render_jack_ports_to_payload_celt(JSList *playback_ports, JSList *playback_srcs, jack_nframes_t nframes, void *packet_payload, jack_nframes_t net_period_up); | render_jack_ports_to_payload_celt(JSList *playback_ports, JSList *playback_srcs, jack_nframes_t nframes, void *packet_payload, jack_nframes_t net_period_up); | ||||
| #endif | |||||
| #if HAVE_OPUS | |||||
| void | |||||
| render_payload_to_jack_ports_opus(void *packet_payload, jack_nframes_t net_period_down, JSList *capture_ports, JSList *capture_srcs, jack_nframes_t nframes); | |||||
| void | |||||
| render_jack_ports_to_payload_opus(JSList *playback_ports, JSList *playback_srcs, jack_nframes_t nframes, void *packet_payload, jack_nframes_t net_period_up); | |||||
| #endif | #endif | ||||
| void | void | ||||
| render_payload_to_jack_ports(int bitdepth, void *packet_payload, jack_nframes_t net_period_down, JSList *capture_ports, JSList *capture_srcs, jack_nframes_t nframes, int dont_htonl_floats); | render_payload_to_jack_ports(int bitdepth, void *packet_payload, jack_nframes_t net_period_down, JSList *capture_ports, JSList *capture_srcs, jack_nframes_t nframes, int dont_htonl_floats); | ||||
| @@ -710,6 +710,7 @@ namespace Jack | |||||
| #endif | #endif | ||||
| #if HAVE_OPUS | #if HAVE_OPUS | ||||
| #define CDO (sizeof(short)) ///< compressed data offset (first 2 bytes are length) | |||||
| NetOpusAudioBuffer::NetOpusAudioBuffer(session_params_t* params, uint32_t nports, char* net_buffer, int kbps) | NetOpusAudioBuffer::NetOpusAudioBuffer(session_params_t* params, uint32_t nports, char* net_buffer, int kbps) | ||||
| :NetAudioBuffer(params, nports, net_buffer) | :NetAudioBuffer(params, nports, net_buffer) | ||||
| @@ -717,10 +718,12 @@ namespace Jack | |||||
| fOpusMode = new OpusCustomMode *[fNPorts]; | fOpusMode = new OpusCustomMode *[fNPorts]; | ||||
| fOpusEncoder = new OpusCustomEncoder *[fNPorts]; | fOpusEncoder = new OpusCustomEncoder *[fNPorts]; | ||||
| fOpusDecoder = new OpusCustomDecoder *[fNPorts]; | fOpusDecoder = new OpusCustomDecoder *[fNPorts]; | ||||
| fCompressedSizesByte = new unsigned short [fNPorts]; | |||||
| memset(fOpusMode, 0, fNPorts * sizeof(OpusCustomMode*)); | memset(fOpusMode, 0, fNPorts * sizeof(OpusCustomMode*)); | ||||
| memset(fOpusEncoder, 0, fNPorts * sizeof(OpusCustomEncoder*)); | memset(fOpusEncoder, 0, fNPorts * sizeof(OpusCustomEncoder*)); | ||||
| memset(fOpusDecoder, 0, fNPorts * sizeof(OpusCustomDecoder*)); | memset(fOpusDecoder, 0, fNPorts * sizeof(OpusCustomDecoder*)); | ||||
| memset(fCompressedSizesByte, 0, fNPorts * sizeof(int)); | |||||
| int error = OPUS_OK; | int error = OPUS_OK; | ||||
| @@ -753,25 +756,29 @@ namespace Jack | |||||
| } | } | ||||
| { | { | ||||
| fCompressedMaxSizeByte = (kbps * params->fPeriodSize * 1024) / (params->fSampleRate * 8); | |||||
| fPeriodSize = params->fPeriodSize; | fPeriodSize = params->fPeriodSize; | ||||
| fCompressedSizeByte = (kbps * params->fPeriodSize * 1024) / (params->fSampleRate * 8); | |||||
| jack_log("NetOpusAudioBuffer fCompressedSizeByte %d", fCompressedSizeByte); | |||||
| jack_log("NetOpusAudioBuffer fCompressedMaxSizeByte %d", fCompressedMaxSizeByte); | |||||
| fCompressedBuffer = new unsigned char* [fNPorts]; | fCompressedBuffer = new unsigned char* [fNPorts]; | ||||
| for (int port_index = 0; port_index < fNPorts; port_index++) { | for (int port_index = 0; port_index < fNPorts; port_index++) { | ||||
| fCompressedBuffer[port_index] = new unsigned char[fCompressedSizeByte]; | |||||
| memset(fCompressedBuffer[port_index], 0, fCompressedSizeByte * sizeof(char)); | |||||
| fCompressedBuffer[port_index] = new unsigned char[fCompressedMaxSizeByte]; | |||||
| memset(fCompressedBuffer[port_index], 0, fCompressedMaxSizeByte * sizeof(char)); | |||||
| } | } | ||||
| int res1 = (fNPorts * fCompressedSizeByte) % PACKET_AVAILABLE_SIZE(params); | |||||
| int res2 = (fNPorts * fCompressedSizeByte) / PACKET_AVAILABLE_SIZE(params); | |||||
| int res1 = (fNPorts * fCompressedMaxSizeByte + CDO) % PACKET_AVAILABLE_SIZE(params); | |||||
| int res2 = (fNPorts * fCompressedMaxSizeByte + CDO) / PACKET_AVAILABLE_SIZE(params); | |||||
| fNumPackets = (res1) ? (res2 + 1) : res2; | fNumPackets = (res1) ? (res2 + 1) : res2; | ||||
| jack_log("NetOpusAudioBuffer res1 = %d res2 = %d", res1, res2); | jack_log("NetOpusAudioBuffer res1 = %d res2 = %d", res1, res2); | ||||
| fSubPeriodBytesSize = fCompressedSizeByte / fNumPackets; | |||||
| fLastSubPeriodBytesSize = fSubPeriodBytesSize + fCompressedSizeByte % fNumPackets; | |||||
| fSubPeriodBytesSize = (fCompressedMaxSizeByte + CDO) / fNumPackets; | |||||
| fLastSubPeriodBytesSize = fSubPeriodBytesSize + (fCompressedMaxSizeByte + CDO) % fNumPackets; | |||||
| if (fNumPackets == 1) { | |||||
| fSubPeriodBytesSize = fLastSubPeriodBytesSize; | |||||
| } | |||||
| jack_log("NetOpusAudioBuffer fNumPackets = %d fSubPeriodBytesSize = %d, fLastSubPeriodBytesSize = %d", fNumPackets, fSubPeriodBytesSize, fLastSubPeriodBytesSize); | jack_log("NetOpusAudioBuffer fNumPackets = %d fSubPeriodBytesSize = %d, fLastSubPeriodBytesSize = %d", fNumPackets, fSubPeriodBytesSize, fLastSubPeriodBytesSize); | ||||
| @@ -797,6 +804,7 @@ namespace Jack | |||||
| } | } | ||||
| delete [] fCompressedBuffer; | delete [] fCompressedBuffer; | ||||
| delete [] fCompressedSizesByte; | |||||
| } | } | ||||
| void NetOpusAudioBuffer::FreeOpus() | void NetOpusAudioBuffer::FreeOpus() | ||||
| @@ -846,10 +854,12 @@ namespace Jack | |||||
| } else { | } else { | ||||
| memset(buffer, 0, fPeriodSize * sizeof(sample_t)); | memset(buffer, 0, fPeriodSize * sizeof(sample_t)); | ||||
| } | } | ||||
| int res = opus_custom_encode_float(fOpusEncoder[port_index], buffer, fPeriodSize, fCompressedBuffer[port_index], fCompressedSizeByte); | |||||
| if (res != fCompressedSizeByte) { | |||||
| jack_error("opus_encode_float error fCompressedSizeByte = %d res = %d", fCompressedSizeByte, res); | |||||
| } | |||||
| int res = opus_custom_encode_float(fOpusEncoder[port_index], buffer, fPeriodSize, fCompressedBuffer[port_index], fCompressedMaxSizeByte); | |||||
| if (res <0 || res >= 65535) { | |||||
| fCompressedSizesByte[port_index] = 0; | |||||
| } else { | |||||
| fCompressedSizesByte[port_index] = res; | |||||
| } | |||||
| } | } | ||||
| // All ports active | // All ports active | ||||
| @@ -860,9 +870,9 @@ namespace Jack | |||||
| { | { | ||||
| for (int port_index = 0; port_index < fNPorts; port_index++) { | for (int port_index = 0; port_index < fNPorts; port_index++) { | ||||
| if (fPortBuffer[port_index]) { | if (fPortBuffer[port_index]) { | ||||
| int res = opus_custom_decode_float(fOpusDecoder[port_index], fCompressedBuffer[port_index], fCompressedSizeByte, fPortBuffer[port_index], fPeriodSize); | |||||
| if (res != OPUS_OK) { | |||||
| jack_error("opus_decode_float error fCompressedSizeByte = %d res = %d", fCompressedSizeByte, res); | |||||
| int res = opus_custom_decode_float(fOpusDecoder[port_index], fCompressedBuffer[port_index], fCompressedSizesByte[port_index], fPortBuffer[port_index], fPeriodSize); | |||||
| if (res < 0 || res != fPeriodSize) { | |||||
| jack_error("opus_decode_float error fCompressedSizeByte = %d res = %d", fCompressedSizesByte[port_index], res); | |||||
| } | } | ||||
| } | } | ||||
| } | } | ||||
| @@ -879,14 +889,19 @@ namespace Jack | |||||
| } | } | ||||
| if (port_num > 0) { | if (port_num > 0) { | ||||
| // Last packet of the cycle | |||||
| if (sub_cycle == fNumPackets - 1) { | |||||
| if (sub_cycle == 0) { | |||||
| for (int port_index = 0; port_index < fNPorts; port_index++) { | for (int port_index = 0; port_index < fNPorts; port_index++) { | ||||
| memcpy(fCompressedBuffer[port_index] + sub_cycle * fSubPeriodBytesSize, fNetBuffer + port_index * fLastSubPeriodBytesSize, fLastSubPeriodBytesSize); | |||||
| size_t len = *((size_t*)(fNetBuffer + port_index * fSubPeriodBytesSize)); | |||||
| fCompressedSizesByte[port_index] = ntohs(len); | |||||
| memcpy(fCompressedBuffer[port_index] + sub_cycle * fSubPeriodBytesSize, fNetBuffer + CDO + port_index * fSubPeriodBytesSize, fSubPeriodBytesSize - CDO); | |||||
| } | |||||
| } else if (sub_cycle == fNumPackets - 1) { | |||||
| for (int port_index = 0; port_index < fNPorts; port_index++) { | |||||
| memcpy(fCompressedBuffer[port_index] + sub_cycle * fSubPeriodBytesSize - CDO, fNetBuffer + port_index * fLastSubPeriodBytesSize, fLastSubPeriodBytesSize); | |||||
| } | } | ||||
| } else { | } else { | ||||
| for (int port_index = 0; port_index < fNPorts; port_index++) { | for (int port_index = 0; port_index < fNPorts; port_index++) { | ||||
| memcpy(fCompressedBuffer[port_index] + sub_cycle * fSubPeriodBytesSize, fNetBuffer + port_index * fSubPeriodBytesSize, fSubPeriodBytesSize); | |||||
| memcpy(fCompressedBuffer[port_index] + sub_cycle * fSubPeriodBytesSize - CDO, fNetBuffer + port_index * fSubPeriodBytesSize, fSubPeriodBytesSize); | |||||
| } | } | ||||
| } | } | ||||
| } | } | ||||
| @@ -896,15 +911,21 @@ namespace Jack | |||||
| int NetOpusAudioBuffer::RenderToNetwork(int sub_cycle, uint32_t port_num) | int NetOpusAudioBuffer::RenderToNetwork(int sub_cycle, uint32_t port_num) | ||||
| { | { | ||||
| // Last packet of the cycle | |||||
| if (sub_cycle == fNumPackets - 1) { | |||||
| if (sub_cycle == 0) { | |||||
| for (int port_index = 0; port_index < fNPorts; port_index++) { | for (int port_index = 0; port_index < fNPorts; port_index++) { | ||||
| memcpy(fNetBuffer + port_index * fLastSubPeriodBytesSize, fCompressedBuffer[port_index] + sub_cycle * fSubPeriodBytesSize, fLastSubPeriodBytesSize); | |||||
| unsigned short len = htons(fCompressedSizesByte[port_index]); | |||||
| memcpy(fNetBuffer + port_index * fSubPeriodBytesSize, &len, CDO); | |||||
| memcpy(fNetBuffer + port_index * fSubPeriodBytesSize + CDO, fCompressedBuffer[port_index], fSubPeriodBytesSize - CDO); | |||||
| } | |||||
| return fNPorts * fSubPeriodBytesSize; | |||||
| } else if (sub_cycle == fNumPackets - 1) { | |||||
| for (int port_index = 0; port_index < fNPorts; port_index++) { | |||||
| memcpy(fNetBuffer + port_index * fLastSubPeriodBytesSize, fCompressedBuffer[port_index] + sub_cycle * fSubPeriodBytesSize - CDO, fLastSubPeriodBytesSize); | |||||
| } | } | ||||
| return fNPorts * fLastSubPeriodBytesSize; | return fNPorts * fLastSubPeriodBytesSize; | ||||
| } else { | } else { | ||||
| for (int port_index = 0; port_index < fNPorts; port_index++) { | for (int port_index = 0; port_index < fNPorts; port_index++) { | ||||
| memcpy(fNetBuffer + port_index * fSubPeriodBytesSize, fCompressedBuffer[port_index] + sub_cycle * fSubPeriodBytesSize, fSubPeriodBytesSize); | |||||
| memcpy(fNetBuffer + port_index * fSubPeriodBytesSize, fCompressedBuffer[port_index] + sub_cycle * fSubPeriodBytesSize - CDO, fSubPeriodBytesSize); | |||||
| } | } | ||||
| return fNPorts * fSubPeriodBytesSize; | return fNPorts * fSubPeriodBytesSize; | ||||
| } | } | ||||
| @@ -419,7 +419,8 @@ namespace Jack | |||||
| OpusCustomEncoder** fOpusEncoder; | OpusCustomEncoder** fOpusEncoder; | ||||
| OpusCustomDecoder** fOpusDecoder; | OpusCustomDecoder** fOpusDecoder; | ||||
| int fCompressedSizeByte; | |||||
| unsigned short *fCompressedSizesByte; | |||||
| int fCompressedMaxSizeByte; | |||||
| int fNumPackets; | int fNumPackets; | ||||
| size_t fLastSubPeriodBytesSize; | size_t fLastSubPeriodBytesSize; | ||||
| @@ -40,7 +40,7 @@ enum JackNetEncoder { | |||||
| JackFloatEncoder = 0, // samples are transmitted as float | JackFloatEncoder = 0, // samples are transmitted as float | ||||
| JackIntEncoder = 1, // samples are transmitted as 16 bits integer | JackIntEncoder = 1, // samples are transmitted as 16 bits integer | ||||
| JackCeltEncoder = 2, // samples are transmitted using CELT codec (http://www.celt-codec.org/) | JackCeltEncoder = 2, // samples are transmitted using CELT codec (http://www.celt-codec.org/) | ||||
| JackOpusEncoder = 2, // samples are transmitted using OPUS codec (http://www.opus-codec.org/) | |||||
| JackOpusEncoder = 3, // samples are transmitted using OPUS codec (http://www.opus-codec.org/) | |||||
| }; | }; | ||||
| typedef struct { | typedef struct { | ||||
| @@ -368,6 +368,11 @@ void netjack_attach( netjack_driver_state_t *netj ) | |||||
| netj->codec_latency = 2 * lookahead; | netj->codec_latency = 2 * lookahead; | ||||
| #endif | #endif | ||||
| } | } | ||||
| if( netj->bitdepth == OPUS_MODE ) { | |||||
| #if HAVE_OPUS | |||||
| netj->opus_mode = opus_custom_mode_create(netj->sample_rate, netj->period_size, NULL); | |||||
| #endif | |||||
| } | |||||
| if (netj->handle_transport_sync) | if (netj->handle_transport_sync) | ||||
| jack_set_sync_callback(netj->client, (JackSyncCallback) net_driver_sync_cb, NULL); | jack_set_sync_callback(netj->client, (JackSyncCallback) net_driver_sync_cb, NULL); | ||||
| @@ -397,6 +402,12 @@ void netjack_attach( netjack_driver_state_t *netj ) | |||||
| #else | #else | ||||
| netj->capture_srcs = jack_slist_append(netj->capture_srcs, celt_decoder_create( netj->celt_mode ) ); | netj->capture_srcs = jack_slist_append(netj->capture_srcs, celt_decoder_create( netj->celt_mode ) ); | ||||
| #endif | #endif | ||||
| #endif | |||||
| } else if( netj->bitdepth == OPUS_MODE ) { | |||||
| #if HAVE_OPUS | |||||
| OpusCustomDecoder *decoder = opus_custom_decoder_create( netj->opus_mode, 1, NULL ); | |||||
| opus_custom_decoder_init(decoder, netj->opus_mode, 1); | |||||
| netj->capture_srcs = jack_slist_append(netj->capture_srcs, decoder ); | |||||
| #endif | #endif | ||||
| } else { | } else { | ||||
| #if HAVE_SAMPLERATE | #if HAVE_SAMPLERATE | ||||
| @@ -448,6 +459,20 @@ void netjack_attach( netjack_driver_state_t *netj ) | |||||
| CELTMode *celt_mode = celt_mode_create( netj->sample_rate, 1, netj->period_size, NULL ); | CELTMode *celt_mode = celt_mode_create( netj->sample_rate, 1, netj->period_size, NULL ); | ||||
| netj->playback_srcs = jack_slist_append(netj->playback_srcs, celt_encoder_create( celt_mode ) ); | netj->playback_srcs = jack_slist_append(netj->playback_srcs, celt_encoder_create( celt_mode ) ); | ||||
| #endif | #endif | ||||
| #endif | |||||
| } else if( netj->bitdepth == OPUS_MODE ) { | |||||
| #if HAVE_OPUS | |||||
| const int kbps = netj->resample_factor; | |||||
| jack_log( "OPUS %dkbps\n", kbps); | |||||
| OpusCustomMode *opus_mode = opus_custom_mode_create( netj->sample_rate, netj->period_size, NULL ); // XXX free me in the end | |||||
| OpusCustomEncoder *oe = opus_custom_encoder_create( opus_mode, 1, NULL ); | |||||
| opus_custom_encoder_ctl(oe, OPUS_SET_BITRATE(kbps*1024)); // bits per second | |||||
| opus_custom_encoder_ctl(oe, OPUS_SET_COMPLEXITY(10)); | |||||
| opus_custom_encoder_ctl(oe, OPUS_SET_SIGNAL(OPUS_SIGNAL_MUSIC)); | |||||
| opus_custom_encoder_ctl(oe, OPUS_SET_SIGNAL(OPUS_APPLICATION_RESTRICTED_LOWDELAY)); | |||||
| opus_custom_encoder_init(oe, opus_mode, 1); | |||||
| netj->playback_srcs = jack_slist_append(netj->playback_srcs, oe ); | |||||
| #endif | #endif | ||||
| } else { | } else { | ||||
| #if HAVE_SAMPLERATE | #if HAVE_SAMPLERATE | ||||
| @@ -492,6 +517,12 @@ void netjack_detach( netjack_driver_state_t *netj ) | |||||
| CELTDecoder * decoder = node->data; | CELTDecoder * decoder = node->data; | ||||
| celt_decoder_destroy(decoder); | celt_decoder_destroy(decoder); | ||||
| } else | } else | ||||
| #endif | |||||
| #if HAVE_OPUS | |||||
| if ( netj->bitdepth == OPUS_MODE ) { | |||||
| OpusCustomDecoder * decoder = node->data; | |||||
| opus_custom_decoder_destroy(decoder); | |||||
| } else | |||||
| #endif | #endif | ||||
| { | { | ||||
| #if HAVE_SAMPLERATE | #if HAVE_SAMPLERATE | ||||
| @@ -516,6 +547,12 @@ void netjack_detach( netjack_driver_state_t *netj ) | |||||
| CELTEncoder * encoder = node->data; | CELTEncoder * encoder = node->data; | ||||
| celt_encoder_destroy(encoder); | celt_encoder_destroy(encoder); | ||||
| } else | } else | ||||
| #endif | |||||
| #if HAVE_OPUS | |||||
| if ( netj->bitdepth == OPUS_MODE ) { | |||||
| OpusCustomEncoder * encoder = node->data; | |||||
| opus_custom_encoder_destroy(encoder); | |||||
| } else | |||||
| #endif | #endif | ||||
| { | { | ||||
| #if HAVE_SAMPLERATE | #if HAVE_SAMPLERATE | ||||
| @@ -531,6 +568,10 @@ void netjack_detach( netjack_driver_state_t *netj ) | |||||
| if( netj->bitdepth == CELT_MODE ) | if( netj->bitdepth == CELT_MODE ) | ||||
| celt_mode_destroy(netj->celt_mode); | celt_mode_destroy(netj->celt_mode); | ||||
| #endif | #endif | ||||
| #if HAVE_OPUS | |||||
| if( netj->bitdepth == OPUS_MODE ) | |||||
| opus_custom_mode_destroy(netj->opus_mode); | |||||
| #endif | |||||
| } | } | ||||
| @@ -588,7 +629,7 @@ netjack_driver_state_t *netjack_init (netjack_driver_state_t *netj, | |||||
| netj->client = client; | netj->client = client; | ||||
| if ((bitdepth != 0) && (bitdepth != 8) && (bitdepth != 16) && (bitdepth != CELT_MODE)) { | |||||
| if ((bitdepth != 0) && (bitdepth != 8) && (bitdepth != 16) && (bitdepth != CELT_MODE) && (bitdepth != OPUS_MODE)) { | |||||
| jack_info ("Invalid bitdepth: %d (8, 16 or 0 for float) !!!", bitdepth); | jack_info ("Invalid bitdepth: %d (8, 16 or 0 for float) !!!", bitdepth); | ||||
| return NULL; | return NULL; | ||||
| } | } | ||||
| @@ -755,6 +796,11 @@ netjack_startup( netjack_driver_state_t *netj ) | |||||
| netj->net_period_down = netj->resample_factor; | netj->net_period_down = netj->resample_factor; | ||||
| netj->net_period_up = netj->resample_factor_up; | netj->net_period_up = netj->resample_factor_up; | ||||
| } else if( netj->bitdepth == OPUS_MODE ) { | |||||
| // Opus mode. | |||||
| // TODO: this is a hack. But i dont want to change the packet header, either | |||||
| netj->net_period_down = (netj->resample_factor * netj->period_size * 1024 / netj->sample_rate / 8) & (~1); | |||||
| netj->net_period_up = (netj->resample_factor_up * netj->period_size * 1024 / netj->sample_rate / 8) & (~1); | |||||
| } else { | } else { | ||||
| netj->net_period_down = (float) netj->period_size / (float) netj->resample_factor; | netj->net_period_down = (float) netj->period_size / (float) netj->resample_factor; | ||||
| netj->net_period_up = (float) netj->period_size / (float) netj->resample_factor_up; | netj->net_period_up = (float) netj->period_size / (float) netj->resample_factor_up; | ||||
| @@ -32,6 +32,11 @@ | |||||
| #include <celt/celt.h> | #include <celt/celt.h> | ||||
| #endif | #endif | ||||
| #if HAVE_OPUS | |||||
| #include <opus/opus.h> | |||||
| #include <opus/opus_custom.h> | |||||
| #endif | |||||
| #ifdef __cplusplus | #ifdef __cplusplus | ||||
| extern "C" | extern "C" | ||||
| { | { | ||||
| @@ -111,6 +116,9 @@ extern "C" | |||||
| struct _packet_cache * packcache; | struct _packet_cache * packcache; | ||||
| #if HAVE_CELT | #if HAVE_CELT | ||||
| CELTMode *celt_mode; | CELTMode *celt_mode; | ||||
| #endif | |||||
| #if HAVE_OPUS | |||||
| OpusCustomMode* opus_mode; | |||||
| #endif | #endif | ||||
| }; | }; | ||||
| @@ -72,6 +72,11 @@ | |||||
| #include <celt/celt.h> | #include <celt/celt.h> | ||||
| #endif | #endif | ||||
| #if HAVE_OPUS | |||||
| #include <opus/opus.h> | |||||
| #include <opus/opus_custom.h> | |||||
| #endif | |||||
| #include "netjack_packet.h" | #include "netjack_packet.h" | ||||
| #include "JackError.h" | #include "JackError.h" | ||||
| @@ -129,6 +134,8 @@ int get_sample_size (int bitdepth) | |||||
| //JN: if the former, why not int16_t, if the latter, shouldn't it depend on -c N? | //JN: if the former, why not int16_t, if the latter, shouldn't it depend on -c N? | ||||
| if( bitdepth == CELT_MODE ) | if( bitdepth == CELT_MODE ) | ||||
| return sizeof( unsigned char ); | return sizeof( unsigned char ); | ||||
| if( bitdepth == OPUS_MODE ) | |||||
| return sizeof( unsigned char ); | |||||
| return sizeof (int32_t); | return sizeof (int32_t); | ||||
| } | } | ||||
| @@ -1347,6 +1354,91 @@ render_jack_ports_to_payload_celt (JSList *playback_ports, JSList *playback_srcs | |||||
| } | } | ||||
| #endif | #endif | ||||
| #if HAVE_OPUS | |||||
| #define CDO (sizeof(short)) ///< compressed data offset (first 2 bytes are length) | |||||
| // render functions for Opus. | |||||
| void | |||||
| render_payload_to_jack_ports_opus (void *packet_payload, jack_nframes_t net_period_down, JSList *capture_ports, JSList *capture_srcs, jack_nframes_t nframes) | |||||
| { | |||||
| int chn = 0; | |||||
| JSList *node = capture_ports; | |||||
| JSList *src_node = capture_srcs; | |||||
| unsigned char *packet_bufX = (unsigned char *)packet_payload; | |||||
| while (node != NULL) { | |||||
| jack_port_t *port = (jack_port_t *) node->data; | |||||
| jack_default_audio_sample_t* buf = jack_port_get_buffer (port, nframes); | |||||
| const char *porttype = jack_port_type (port); | |||||
| if (jack_port_is_audio (porttype)) { | |||||
| // audio port, decode opus data. | |||||
| OpusCustomDecoder *decoder = (OpusCustomDecoder*) src_node->data; | |||||
| if( !packet_payload ) | |||||
| memset(buf, 0, nframes * sizeof(float)); | |||||
| else { | |||||
| unsigned short len; | |||||
| memcpy(&len, packet_bufX, CDO); | |||||
| len = ntohs(len); | |||||
| opus_custom_decode_float( decoder, packet_bufX + CDO, len, buf, nframes ); | |||||
| } | |||||
| src_node = jack_slist_next (src_node); | |||||
| } else if (jack_port_is_midi (porttype)) { | |||||
| // midi port, decode midi events | |||||
| // convert the data buffer to a standard format (uint32_t based) | |||||
| unsigned int buffer_size_uint32 = net_period_down / 2; | |||||
| uint32_t * buffer_uint32 = (uint32_t*) packet_bufX; | |||||
| if( packet_payload ) | |||||
| decode_midi_buffer (buffer_uint32, buffer_size_uint32, buf); | |||||
| } | |||||
| packet_bufX = (packet_bufX + net_period_down); | |||||
| node = jack_slist_next (node); | |||||
| chn++; | |||||
| } | |||||
| } | |||||
| void | |||||
| render_jack_ports_to_payload_opus (JSList *playback_ports, JSList *playback_srcs, jack_nframes_t nframes, void *packet_payload, jack_nframes_t net_period_up) | |||||
| { | |||||
| int chn = 0; | |||||
| JSList *node = playback_ports; | |||||
| JSList *src_node = playback_srcs; | |||||
| unsigned char *packet_bufX = (unsigned char *)packet_payload; | |||||
| while (node != NULL) { | |||||
| jack_port_t *port = (jack_port_t *) node->data; | |||||
| jack_default_audio_sample_t* buf = jack_port_get_buffer (port, nframes); | |||||
| const char *porttype = jack_port_type (port); | |||||
| if (jack_port_is_audio (porttype)) { | |||||
| // audio port, encode opus data. | |||||
| int encoded_bytes; | |||||
| float *floatbuf = alloca (sizeof(float) * nframes ); | |||||
| memcpy( floatbuf, buf, nframes * sizeof(float) ); | |||||
| OpusCustomEncoder *encoder = (OpusCustomEncoder*) src_node->data; | |||||
| encoded_bytes = opus_custom_encode_float( encoder, floatbuf, nframes, packet_bufX + CDO, net_period_up - CDO ); | |||||
| unsigned short len = htons(encoded_bytes); | |||||
| memcpy(packet_bufX, &len, CDO); | |||||
| src_node = jack_slist_next( src_node ); | |||||
| } else if (jack_port_is_midi (porttype)) { | |||||
| // encode midi events from port to packet | |||||
| // convert the data buffer to a standard format (uint32_t based) | |||||
| unsigned int buffer_size_uint32 = net_period_up / 2; | |||||
| uint32_t * buffer_uint32 = (uint32_t*) packet_bufX; | |||||
| encode_midi_buffer (buffer_uint32, buffer_size_uint32, buf); | |||||
| } | |||||
| packet_bufX = (packet_bufX + net_period_up); | |||||
| node = jack_slist_next (node); | |||||
| chn++; | |||||
| } | |||||
| } | |||||
| #endif | |||||
| /* Wrapper functions with bitdepth argument... */ | /* Wrapper functions with bitdepth argument... */ | ||||
| void | void | ||||
| render_payload_to_jack_ports (int bitdepth, void *packet_payload, jack_nframes_t net_period_down, JSList *capture_ports, JSList *capture_srcs, jack_nframes_t nframes, int dont_htonl_floats) | render_payload_to_jack_ports (int bitdepth, void *packet_payload, jack_nframes_t net_period_down, JSList *capture_ports, JSList *capture_srcs, jack_nframes_t nframes, int dont_htonl_floats) | ||||
| @@ -1358,6 +1450,10 @@ render_payload_to_jack_ports (int bitdepth, void *packet_payload, jack_nframes_t | |||||
| #if HAVE_CELT | #if HAVE_CELT | ||||
| else if (bitdepth == CELT_MODE) | else if (bitdepth == CELT_MODE) | ||||
| render_payload_to_jack_ports_celt (packet_payload, net_period_down, capture_ports, capture_srcs, nframes); | render_payload_to_jack_ports_celt (packet_payload, net_period_down, capture_ports, capture_srcs, nframes); | ||||
| #endif | |||||
| #if HAVE_OPUS | |||||
| else if (bitdepth == OPUS_MODE) | |||||
| render_payload_to_jack_ports_opus (packet_payload, net_period_down, capture_ports, capture_srcs, nframes); | |||||
| #endif | #endif | ||||
| else | else | ||||
| render_payload_to_jack_ports_float (packet_payload, net_period_down, capture_ports, capture_srcs, nframes, dont_htonl_floats); | render_payload_to_jack_ports_float (packet_payload, net_period_down, capture_ports, capture_srcs, nframes, dont_htonl_floats); | ||||
| @@ -1373,6 +1469,10 @@ render_jack_ports_to_payload (int bitdepth, JSList *playback_ports, JSList *play | |||||
| #if HAVE_CELT | #if HAVE_CELT | ||||
| else if (bitdepth == CELT_MODE) | else if (bitdepth == CELT_MODE) | ||||
| render_jack_ports_to_payload_celt (playback_ports, playback_srcs, nframes, packet_payload, net_period_up); | render_jack_ports_to_payload_celt (playback_ports, playback_srcs, nframes, packet_payload, net_period_up); | ||||
| #endif | |||||
| #if HAVE_OPUS | |||||
| else if (bitdepth == OPUS_MODE) | |||||
| render_jack_ports_to_payload_opus (playback_ports, playback_srcs, nframes, packet_payload, net_period_up); | |||||
| #endif | #endif | ||||
| else | else | ||||
| render_jack_ports_to_payload_float (playback_ports, playback_srcs, nframes, packet_payload, net_period_up, dont_htonl_floats); | render_jack_ports_to_payload_float (playback_ports, playback_srcs, nframes, packet_payload, net_period_up, dont_htonl_floats); | ||||
| @@ -40,6 +40,7 @@ extern "C" | |||||
| // The Packet Header. | // The Packet Header. | ||||
| #define CELT_MODE 1000 // Magic bitdepth value that indicates CELT compression | #define CELT_MODE 1000 // Magic bitdepth value that indicates CELT compression | ||||
| #define OPUS_MODE 999 // Magic bitdepth value that indicates OPUS compression | |||||
| #define MASTER_FREEWHEELS 0x80000000 | #define MASTER_FREEWHEELS 0x80000000 | ||||
| typedef struct _jacknet_packet_header jacknet_packet_header; | typedef struct _jacknet_packet_header jacknet_packet_header; | ||||
| @@ -62,6 +62,11 @@ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. | |||||
| #include <celt/celt.h> | #include <celt/celt.h> | ||||
| #endif | #endif | ||||
| #if HAVE_OPUS | |||||
| #include <opus/opus.h> | |||||
| #include <opus/opus_custom.h> | |||||
| #endif | |||||
| #include <math.h> | #include <math.h> | ||||
| JSList *capture_ports = NULL; | JSList *capture_ports = NULL; | ||||
| @@ -147,6 +152,16 @@ alloc_ports (int n_capture_audio, int n_playback_audio, int n_capture_midi, int | |||||
| CELTMode *celt_mode = celt_mode_create( jack_get_sample_rate( client ), 1, jack_get_buffer_size(client), NULL ); | CELTMode *celt_mode = celt_mode_create( jack_get_sample_rate( client ), 1, jack_get_buffer_size(client), NULL ); | ||||
| capture_srcs = jack_slist_append(capture_srcs, celt_decoder_create( celt_mode ) ); | capture_srcs = jack_slist_append(capture_srcs, celt_decoder_create( celt_mode ) ); | ||||
| #endif | #endif | ||||
| #endif | |||||
| } else if (bitdepth == 999) { | |||||
| #if HAVE_OPUS | |||||
| int err; | |||||
| OpusCustomMode *opus_mode = opus_custom_mode_create(jack_get_sample_rate( client ), jack_get_buffer_size(client), &err); | |||||
| if (err != OPUS_OK) { printf("OPUS MODE FAILED\n"); } | |||||
| OpusCustomDecoder *decoder = opus_custom_decoder_create(opus_mode, 1, &err); | |||||
| if (err != OPUS_OK) { printf("OPUS DECODER FAILED\n"); } | |||||
| opus_custom_decoder_init(decoder, opus_mode, 1); | |||||
| capture_srcs = jack_slist_append(capture_srcs, decoder); | |||||
| #endif | #endif | ||||
| } else { | } else { | ||||
| #if HAVE_SAMPLERATE | #if HAVE_SAMPLERATE | ||||
| @@ -189,6 +204,22 @@ alloc_ports (int n_capture_audio, int n_playback_audio, int n_capture_midi, int | |||||
| CELTMode *celt_mode = celt_mode_create( jack_get_sample_rate (client), 1, jack_get_buffer_size(client), NULL ); | CELTMode *celt_mode = celt_mode_create( jack_get_sample_rate (client), 1, jack_get_buffer_size(client), NULL ); | ||||
| playback_srcs = jack_slist_append(playback_srcs, celt_encoder_create( celt_mode ) ); | playback_srcs = jack_slist_append(playback_srcs, celt_encoder_create( celt_mode ) ); | ||||
| #endif | #endif | ||||
| #endif | |||||
| } else if( bitdepth == 999 ) { | |||||
| #if HAVE_OPUS | |||||
| const int kbps = factor; | |||||
| printf("new opus encoder %d kbps\n", kbps); | |||||
| int err; | |||||
| OpusCustomMode *opus_mode = opus_custom_mode_create(jack_get_sample_rate (client), jack_get_buffer_size(client), &err ); // XXX free me | |||||
| if (err != OPUS_OK) { printf("OPUS MODE FAILED\n"); } | |||||
| OpusCustomEncoder *oe = opus_custom_encoder_create( opus_mode, 1, &err ); | |||||
| if (err != OPUS_OK) { printf("OPUS ENCODER FAILED\n"); } | |||||
| opus_custom_encoder_ctl(oe, OPUS_SET_BITRATE(kbps*1024)); // bits per second | |||||
| opus_custom_encoder_ctl(oe, OPUS_SET_COMPLEXITY(10)); | |||||
| opus_custom_encoder_ctl(oe, OPUS_SET_SIGNAL(OPUS_SIGNAL_MUSIC)); | |||||
| opus_custom_encoder_ctl(oe, OPUS_SET_SIGNAL(OPUS_APPLICATION_RESTRICTED_LOWDELAY)); | |||||
| opus_custom_encoder_init(oe, opus_mode, 1); | |||||
| playback_srcs = jack_slist_append(playback_srcs, oe); | |||||
| #endif | #endif | ||||
| } else { | } else { | ||||
| #if HAVE_SAMPLERATE | #if HAVE_SAMPLERATE | ||||
| @@ -269,7 +300,7 @@ process (jack_nframes_t nframes, void *arg) | |||||
| uint32_t *rx_packet_ptr; | uint32_t *rx_packet_ptr; | ||||
| jack_time_t packet_recv_timestamp; | jack_time_t packet_recv_timestamp; | ||||
| if( bitdepth == 1000 ) | |||||
| if( bitdepth == 1000 || bitdepth == 999) | |||||
| net_period = (factor * jack_get_buffer_size(client) * 1024 / jack_get_sample_rate(client) / 8) & (~1) ; | net_period = (factor * jack_get_buffer_size(client) * 1024 / jack_get_sample_rate(client) / 8) & (~1) ; | ||||
| else | else | ||||
| net_period = (float) nframes / (float) factor; | net_period = (float) nframes / (float) factor; | ||||
| @@ -519,6 +550,7 @@ printUsage () | |||||
| " -B <bind port> - reply port, for use in NAT environments\n" | " -B <bind port> - reply port, for use in NAT environments\n" | ||||
| " -b <bitdepth> - Set transport to use 16bit or 8bit\n" | " -b <bitdepth> - Set transport to use 16bit or 8bit\n" | ||||
| " -c <kbits> - Use CELT encoding with <kbits> kbits per channel\n" | " -c <kbits> - Use CELT encoding with <kbits> kbits per channel\n" | ||||
| " -P <kbits> - Use Opus encoding with <kbits> kbits per channel\n" | |||||
| " -m <mtu> - Assume this mtu for the link\n" | " -m <mtu> - Assume this mtu for the link\n" | ||||
| " -R <N> - Redundancy: send out packets N times.\n" | " -R <N> - Redundancy: send out packets N times.\n" | ||||
| " -e - skip host-to-network endianness conversion\n" | " -e - skip host-to-network endianness conversion\n" | ||||
| @@ -564,7 +596,7 @@ main (int argc, char *argv[]) | |||||
| sprintf(client_name, "netjack"); | sprintf(client_name, "netjack"); | ||||
| sprintf(peer_ip, "localhost"); | sprintf(peer_ip, "localhost"); | ||||
| while ((c = getopt (argc, argv, ":h:H:o:i:O:I:n:p:r:B:b:c:m:R:e:N:s:")) != -1) { | |||||
| while ((c = getopt (argc, argv, ":h:H:o:i:O:I:n:p:r:B:b:c:m:R:e:N:s:P:")) != -1) { | |||||
| switch (c) { | switch (c) { | ||||
| case 'h': | case 'h': | ||||
| printUsage(); | printUsage(); | ||||
| @@ -613,6 +645,15 @@ main (int argc, char *argv[]) | |||||
| #else | #else | ||||
| printf( "not built with celt support\n" ); | printf( "not built with celt support\n" ); | ||||
| exit(10); | exit(10); | ||||
| #endif | |||||
| break; | |||||
| case 'P': | |||||
| #if HAVE_OPUS | |||||
| bitdepth = 999; | |||||
| factor = atoi (optarg); | |||||
| #else | |||||
| printf( "not built with opus support\n" ); | |||||
| exit(10); | |||||
| #endif | #endif | ||||
| break; | break; | ||||
| case 'm': | case 'm': | ||||
| @@ -689,7 +730,7 @@ main (int argc, char *argv[]) | |||||
| alloc_ports (capture_channels_audio, playback_channels_audio, capture_channels_midi, playback_channels_midi); | alloc_ports (capture_channels_audio, playback_channels_audio, capture_channels_midi, playback_channels_midi); | ||||
| if( bitdepth == 1000 ) | |||||
| if( bitdepth == 1000 || bitdepth == 999) | |||||
| net_period = (factor * jack_get_buffer_size(client) * 1024 / jack_get_sample_rate(client) / 8) & (~1) ; | net_period = (factor * jack_get_buffer_size(client) * 1024 / jack_get_sample_rate(client) / 8) & (~1) ; | ||||
| else | else | ||||
| net_period = ceilf((float) jack_get_buffer_size (client) / (float) factor); | net_period = ceilf((float) jack_get_buffer_size (client) / (float) factor); | ||||
| @@ -126,12 +126,12 @@ def build(bld): | |||||
| if bld.env['IS_LINUX'] or bld.env['IS_MACOSX']: | if bld.env['IS_LINUX'] or bld.env['IS_MACOSX']: | ||||
| prog = bld(features = 'c cprogram') | prog = bld(features = 'c cprogram') | ||||
| prog.includes = os_incdir + ['../common/jack', '../common'] | |||||
| prog.includes = os_incdir + ['.', '..', '../common/jack', '../common'] | |||||
| prog.source = ['netsource.c', '../common/netjack_packet.c'] | prog.source = ['netsource.c', '../common/netjack_packet.c'] | ||||
| prog.env.append_value("CFLAGS", "-DNO_JACK_ERROR") | prog.env.append_value("CFLAGS", "-DNO_JACK_ERROR") | ||||
| prog.use = ['CELT', 'SAMPLERATE'] | |||||
| prog.use = ['clientlib'] | |||||
| prog.use = ['CELT', 'SAMPLERATE', 'OPUS', 'clientlib'] | |||||
| prog.target = 'jack_netsource' | prog.target = 'jack_netsource' | ||||
| prog.defines = ['HAVE_CONFIG_H'] | |||||
| if bld.env['IS_LINUX'] and bld.env['BUILD_EXAMPLE_ALSA_IO']: | if bld.env['IS_LINUX'] and bld.env['BUILD_EXAMPLE_ALSA_IO']: | ||||
| prog = bld(features = 'c cprogram') | prog = bld(features = 'c cprogram') | ||||