Skip to content

malisimat/ninjam

 
 

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

5,377 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

libninjam — NINJAM Client Static Library

A minimal, buildable extraction of the NINJAM client engine (NJClient) as a static library. All GUI code, audio backends, and server code have been removed. Only the network protocol, audio encode/decode pipeline, and session management remain.

The public API surface is ninjam/njclient.h. Everything else in this repository is an implementation detail.


Repository Layout

ninjam/
  njclient.h        ← Public API (the only header you consume)
  njclient.cpp      ← Full engine implementation
  netmsg.h/.cpp     ← Network message framing
  mpb.h/.cpp        ← NINJAM protocol message parsers/builders
WDL/
  vorbisencdec.h    ← OGG Vorbis encoder/decoder wrapper
  jnetlib/          ← Minimal TCP networking (asyncdns, connection, util)
  *.h / sha.cpp     ← WDL support headers and crypto
njclient.vcxproj    ← MSBuild project (Visual Studio / MSBuild)
deps.props          ← OGG / Vorbis include path configuration

Dependencies

Dependency Purpose Install
libvorbis + libvorbisenc OGG Vorbis audio codec vcpkg install libvorbis:x64-windows-static
libogg OGG bitstream framing vcpkg install libogg:x64-windows-static

vcpkg (recommended): install vcpkg and run:

vcpkg install libogg:x64-windows-static libvorbis:x64-windows-static

Manual: download pre-built Windows binaries from https://xiph.org/downloads/ and configure the paths in deps.props.


Building

MSBuild (Windows)

  1. Configure dependency paths in deps.props:

    <OggIncludeDir>C:\path\to\vcpkg\installed\x64-windows-static\include</OggIncludeDir>
    <VorbisIncludeDir>C:\path\to\vcpkg\installed\x64-windows-static\include</VorbisIncludeDir>

    With vcpkg both headers live under the same include directory, so both properties point to the same path.

  2. Build from the command line:

    "C:\Program Files\Microsoft Visual Studio\18\Community\MSBuild\Current\Bin\MSBuild.exe" ^
        njclient.vcxproj /p:Configuration=Release /p:Platform=x64

    Build both runtime variants explicitly:

    rem Release MT
    "C:\Program Files\Microsoft Visual Studio\18\Community\MSBuild\Current\Bin\MSBuild.exe" ^
        njclient.vcxproj /p:Configuration=Release /p:Platform=x64 /p:NinjamRuntimeMode=Static
    
    rem Release MD
    "C:\Program Files\Microsoft Visual Studio\18\Community\MSBuild\Current\Bin\MSBuild.exe" ^
        njclient.vcxproj /p:Configuration=Release /p:Platform=x64 /p:NinjamRuntimeMode=Dynamic

    Or for a Debug build:

    "C:\Program Files\Microsoft Visual Studio\18\Community\MSBuild\Current\Bin\MSBuild.exe" ^
        njclient.vcxproj /p:Configuration=Debug /p:Platform=x64

    And for both Debug runtime variants:

    rem Debug MT
    "C:\Program Files\Microsoft Visual Studio\18\Community\MSBuild\Current\Bin\MSBuild.exe" ^
        njclient.vcxproj /p:Configuration=Debug /p:Platform=x64 /p:NinjamRuntimeMode=Static
    
    rem Debug MD
    "C:\Program Files\Microsoft Visual Studio\18\Community\MSBuild\Current\Bin\MSBuild.exe" ^
        njclient.vcxproj /p:Configuration=Debug /p:Platform=x64 /p:NinjamRuntimeMode=Dynamic
  3. Output:

    • bin\x64\Release\MT\njclient.lib
    • bin\x64\Release\MD\njclient.lib
    • bin\x64\Debug\MT\njclient.lib
    • bin\x64\Debug\MD\njclient.lib

Makefile (Linux / macOS)

CXX     = g++
CC      = gcc
AR      = ar
CFLAGS  = -O2 -I. $(shell pkg-config --cflags ogg vorbis vorbisenc)
CXXFLAGS = $(CFLAGS) -std=c++11

SRCS = ninjam/njclient.cpp ninjam/netmsg.cpp ninjam/mpb.cpp \
       WDL/sha.cpp WDL/rng.cpp \
       WDL/jnetlib/connection.cpp WDL/jnetlib/asyncdns.cpp WDL/jnetlib/util.cpp

SRCS_C = WDL/win32_utf8.c

OBJS = $(SRCS:.cpp=.o) $(SRCS_C:.c=.o)

libnjclient.a: $(OBJS)
	$(AR) rcs $@ $^

%.o: %.cpp
	$(CXX) $(CXXFLAGS) -c $< -o $@

%.o: %.c
	$(CC) $(CFLAGS) -c $< -o $@

clean:
	rm -f $(OBJS) libnjclient.a

Integrating into Your Project

1. Include the header

#include "njclient.h"

2. Connect to a server

NJClient client;

// Called once when user accepts the license agreement:
client.LicenseAgreementCallback = [](void *, const char *) { return 1; };

// Receive chat messages:
client.ChatMessage_Callback = [](void *, NJClient *, const char **parms, int n) {
    // parms[0] = "MSG", parms[1] = username, parms[2] = text
};

client.Connect("hostname.example.com:2049", "username", "password");

3. Drive the Run loop (separate thread or timer, ≤50 ms cadence)

// In your network/UI thread:
while (!client.Run()); // Run() returns 0 when it wants to be called again immediately

4. Drive audio from your audio thread

// Called from your audio callback (e.g. JACK, PortAudio, CoreAudio):
// inbuf[ch] and outbuf[ch] are non-interleaved float arrays of `len` samples.
client.AudioProc(inbuf, innch, outbuf, outnch, len, sampleRate);

AudioProc handles all encoding, decoding, mixing, and metering internally. It is safe to call from a dedicated audio thread concurrently with Run().

5. Set up a local channel for transmit

// Register channel 0 as a mono input from hardware input channel 0:
client.SetLocalChannelInfo(
    /*ch*/       0,
    /*name*/     "my channel",
    /*setsrcch*/ true,  /*srcch*/ 0,       // input channel index
    /*setbr*/    true,  /*br*/   64,        // kbps
    /*setbcast*/ true,  /*bcast*/true       // broadcasting on
);

6. Query remote user state (for a custom UI)

int nusers = client.GetNumUsers();
for (int u = 0; u < nusers; u++) {
    float vol, pan;
    bool mute;
    const char *name = client.GetUserState(u, &vol, &pan, &mute);

    for (int ci = 0; (ci = client.EnumUserChannels(u, ci)) >= 0; ) {
        bool sub;
        const char *chname = client.GetUserChannelState(u, ci, &sub);
    }
}

Notes

Thread Safety

  • AudioProc() must be called from your audio thread only. No mutex is required by the caller.
  • Run() must be called from a single dedicated thread (not the audio thread).
  • All other Get*/Set* methods on remote channel state should be called with client.m_remotechannel_rd_mutex held if called outside the Run() thread.

Working directory

NJClient writes temporary .OGGv segment files during a session. Call SetWorkDir() before Connect():

client.SetWorkDir("/tmp/ninjam-work");

Set config_savelocalaudio = -1 to delete segment files as soon as they are decoded (saves disk space).

Transmit support

Transmit (encoding and uploading local audio) is compiled in by default. To build a receive-only client, define NJCLIENT_NO_XMIT_SUPPORT in your compiler flags.

Default port

NINJAM servers listen on TCP port 2049 by default. Pass it as part of the hostname string: "hostname:2049".

Sample rate

NJClient is sample-rate agnostic. Pass your actual hardware sample rate to AudioProc(). Common values: 44100, 48000.

License

This code is licensed under the GNU General Public License v2 or later (see LICENSE). The WDL headers and JNetLib are provided under a permissive Zlib/WDL license. If you link this library into a proprietary project, the GPL terms apply to the linked result.

About

NINJAM server, clients, autosong, etc

Resources

License

Stars

Watchers

Forks

Packages

 
 
 

Contributors

Languages

  • C++ 75.3%
  • C 24.7%