| @@ -37,7 +37,7 @@ extern "C" | |||
| JackFloatEncoder = 0, | |||
| JackIntEncoder = 1, | |||
| JackCeltEncoder = 2, | |||
| JackMaxEncoder = 3 | |||
| JackOpusEncoder = 3 | |||
| }; | |||
| typedef struct { | |||
| @@ -551,6 +551,11 @@ struct JackNetExtSlave : public JackNetSlaveInterface, public JackRunnableInterf | |||
| return -1; | |||
| } | |||
| if ((fParams.fSampleEncoder == JackOpusEncoder) && (fParams.fKBps == 0)) { | |||
| jack_error("Opus encoder with 0 for kps..."); | |||
| return -1; | |||
| } | |||
| // Check latency | |||
| if (fParams.fNetworkLatency > 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> | |||
| #endif | |||
| #if HAVE_OPUS | |||
| #include <opus/opus.h> | |||
| #include <opus/opus_custom.h> | |||
| #endif | |||
| #define MIN(x,y) ((x)<(y) ? (x) : (y)) | |||
| using namespace std; | |||
| @@ -146,6 +151,13 @@ int JackNetOneDriver::AllocPorts() | |||
| #endif | |||
| celt_mode_info(celt_mode, CELT_GET_LOOKAHEAD, &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 | |||
| } else { | |||
| #if HAVE_SAMPLERATE | |||
| @@ -191,6 +203,22 @@ int JackNetOneDriver::AllocPorts() | |||
| 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)); | |||
| #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 | |||
| } else { | |||
| #if HAVE_SAMPLERATE | |||
| @@ -450,6 +478,28 @@ JackNetOneDriver::FreePorts () | |||
| celt_decoder_destroy(dec); | |||
| } | |||
| 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 | |||
| } else { | |||
| #if HAVE_SAMPLERATE | |||
| @@ -724,6 +774,98 @@ JackNetOneDriver::render_jack_ports_to_payload_celt (JSList *playback_ports, JSL | |||
| } | |||
| #endif | |||
| #if HAVE_OPUS | |||
| // 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 { | |||
| #define CDO (sizeof(size_t)) ///< compressed data offset (first 4 bytes are length) | |||
| size_t len; | |||
| memcpy(&len, packet_bufX, sizeof(size_t)); | |||
| len = ntohl(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 ); | |||
| size_t len = htonl(encoded_bytes); | |||
| memcpy(packet_bufX, &len, sizeof(size_t)); | |||
| 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... */ | |||
| 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) | |||
| @@ -732,6 +874,11 @@ JackNetOneDriver::render_payload_to_jack_ports (int bitdepth, void *packet_paylo | |||
| if (bitdepth == CELT_MODE) | |||
| render_payload_to_jack_ports_celt (packet_payload, net_period_down, capture_ports, capture_srcs, nframes); | |||
| 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 | |||
| 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) | |||
| render_jack_ports_to_payload_celt (playback_ports, playback_srcs, nframes, packet_payload, net_period_up); | |||
| 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 | |||
| 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 | |||
| 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); | |||
| #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 | |||
| 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); | |||
| @@ -904,6 +1060,17 @@ extern "C" | |||
| #endif | |||
| 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': | |||
| handle_transport_sync = param->value.ui; | |||
| 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); | |||
| 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); | |||
| #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 | |||
| 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); | |||
| @@ -40,7 +40,7 @@ enum JackNetEncoder { | |||
| JackFloatEncoder = 0, // samples are transmitted as float | |||
| JackIntEncoder = 1, // samples are transmitted as 16 bits integer | |||
| 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 { | |||
| @@ -368,6 +368,11 @@ void netjack_attach( netjack_driver_state_t *netj ) | |||
| netj->codec_latency = 2 * lookahead; | |||
| #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) | |||
| 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 | |||
| netj->capture_srcs = jack_slist_append(netj->capture_srcs, celt_decoder_create( netj->celt_mode ) ); | |||
| #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 | |||
| } else { | |||
| #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 ); | |||
| netj->playback_srcs = jack_slist_append(netj->playback_srcs, celt_encoder_create( celt_mode ) ); | |||
| #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 | |||
| } else { | |||
| #if HAVE_SAMPLERATE | |||
| @@ -492,6 +517,12 @@ void netjack_detach( netjack_driver_state_t *netj ) | |||
| CELTDecoder * decoder = node->data; | |||
| celt_decoder_destroy(decoder); | |||
| } else | |||
| #endif | |||
| #if HAVE_OPUS | |||
| if ( netj->bitdepth == OPUS_MODE ) { | |||
| OpusCustomDecoder * decoder = node->data; | |||
| opus_custom_decoder_destroy(decoder); | |||
| } else | |||
| #endif | |||
| { | |||
| #if HAVE_SAMPLERATE | |||
| @@ -516,6 +547,12 @@ void netjack_detach( netjack_driver_state_t *netj ) | |||
| CELTEncoder * encoder = node->data; | |||
| celt_encoder_destroy(encoder); | |||
| } else | |||
| #endif | |||
| #if HAVE_OPUS | |||
| if ( netj->bitdepth == OPUS_MODE ) { | |||
| OpusCustomEncoder * encoder = node->data; | |||
| opus_custom_encoder_destroy(encoder); | |||
| } else | |||
| #endif | |||
| { | |||
| #if HAVE_SAMPLERATE | |||
| @@ -531,6 +568,10 @@ void netjack_detach( netjack_driver_state_t *netj ) | |||
| if( netj->bitdepth == CELT_MODE ) | |||
| celt_mode_destroy(netj->celt_mode); | |||
| #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; | |||
| 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); | |||
| return NULL; | |||
| } | |||
| @@ -755,6 +796,11 @@ netjack_startup( netjack_driver_state_t *netj ) | |||
| netj->net_period_down = netj->resample_factor; | |||
| 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 { | |||
| 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; | |||
| @@ -32,6 +32,11 @@ | |||
| #include <celt/celt.h> | |||
| #endif | |||
| #if HAVE_OPUS | |||
| #include <opus/opus.h> | |||
| #include <opus/opus_custom.h> | |||
| #endif | |||
| #ifdef __cplusplus | |||
| extern "C" | |||
| { | |||
| @@ -111,6 +116,9 @@ extern "C" | |||
| struct _packet_cache * packcache; | |||
| #if HAVE_CELT | |||
| CELTMode *celt_mode; | |||
| #endif | |||
| #if HAVE_OPUS | |||
| OpusCustomMode* opus_mode; | |||
| #endif | |||
| }; | |||
| @@ -72,6 +72,11 @@ | |||
| #include <celt/celt.h> | |||
| #endif | |||
| #if HAVE_OPUS | |||
| #include <opus/opus.h> | |||
| #include <opus/opus_custom.h> | |||
| #endif | |||
| #include "netjack_packet.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? | |||
| if( bitdepth == CELT_MODE ) | |||
| return sizeof( unsigned char ); | |||
| if( bitdepth == OPUS_MODE ) | |||
| return sizeof( unsigned char ); | |||
| return sizeof (int32_t); | |||
| } | |||
| @@ -1347,6 +1354,91 @@ render_jack_ports_to_payload_celt (JSList *playback_ports, JSList *playback_srcs | |||
| } | |||
| #endif | |||
| #if HAVE_OPUS | |||
| // 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 { | |||
| #define CDO (sizeof(size_t)) ///< compressed data offset (first 4 bytes are length) | |||
| size_t len; | |||
| memcpy(&len, packet_bufX, sizeof(size_t)); | |||
| len = ntohl(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 ); | |||
| size_t len = htonl(encoded_bytes); | |||
| memcpy(packet_bufX, &len, sizeof(size_t)); | |||
| 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... */ | |||
| 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) | |||
| @@ -1358,6 +1450,10 @@ render_payload_to_jack_ports (int bitdepth, void *packet_payload, jack_nframes_t | |||
| #if HAVE_CELT | |||
| else if (bitdepth == CELT_MODE) | |||
| 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 | |||
| else | |||
| 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 | |||
| else if (bitdepth == CELT_MODE) | |||
| 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 | |||
| else | |||
| 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. | |||
| #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 | |||
| 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> | |||
| #endif | |||
| #if HAVE_OPUS | |||
| #include <opus/opus.h> | |||
| #include <opus/opus_custom.h> | |||
| #endif | |||
| #include <math.h> | |||
| 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 ); | |||
| capture_srcs = jack_slist_append(capture_srcs, celt_decoder_create( celt_mode ) ); | |||
| #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 | |||
| } else { | |||
| #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 ); | |||
| playback_srcs = jack_slist_append(playback_srcs, celt_encoder_create( celt_mode ) ); | |||
| #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 | |||
| } else { | |||
| #if HAVE_SAMPLERATE | |||
| @@ -269,7 +300,7 @@ process (jack_nframes_t nframes, void *arg) | |||
| uint32_t *rx_packet_ptr; | |||
| 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) ; | |||
| else | |||
| net_period = (float) nframes / (float) factor; | |||
| @@ -519,6 +550,7 @@ printUsage () | |||
| " -B <bind port> - reply port, for use in NAT environments\n" | |||
| " -b <bitdepth> - Set transport to use 16bit or 8bit\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" | |||
| " -R <N> - Redundancy: send out packets N times.\n" | |||
| " -e - skip host-to-network endianness conversion\n" | |||
| @@ -564,7 +596,7 @@ main (int argc, char *argv[]) | |||
| sprintf(client_name, "netjack"); | |||
| 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) { | |||
| case 'h': | |||
| printUsage(); | |||
| @@ -613,6 +645,15 @@ main (int argc, char *argv[]) | |||
| #else | |||
| printf( "not built with celt support\n" ); | |||
| 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 | |||
| break; | |||
| 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); | |||
| if( bitdepth == 1000 ) | |||
| if( bitdepth == 1000 || bitdepth == 999) | |||
| net_period = (factor * jack_get_buffer_size(client) * 1024 / jack_get_sample_rate(client) / 8) & (~1) ; | |||
| else | |||
| net_period = ceilf((float) jack_get_buffer_size (client) / (float) factor); | |||