Example of using JACK with juce


#1

Below is some modified code from mammut. Feel free to use code from it.

http://www.notam02.no/~kjetism/jucejackexample.cpp

/*

This is a quick and not that nice (but good enough) example on how to make sound with juce,
including jack support. It is made in such a way, that changing an old AudioIODeviceCallback subclass
into supporting jack should be very simple.

The constructor first checks if the jack server is running. If not, it will use juce's
sound API instead. Erik de Castro Lopo's libsamplerate is used for resampling, in case
resampling is needed.

Instructions:
* The methods getSourceLength, getSourceData, getSourceRate, getSourceNumChannels, sourceInit,
  and sourceCleanup must be provided.
* First call juceplay_init()
* To make sound, call juceplay_start()
* To stop the sound, call juceplay_stop()

I'm sorry about all the non-C++ stuff. I'm not a big C++ fan.

*/


#include "juce.h"
#include "jackplay.h"
#include <samplerate.h>

#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <string.h>

#include <jack/jack.h>


#define SRC_QUALITY SRC_SINC_BEST_QUALITY




class JucePlayer : public AudioIODeviceCallback
{
  AudioDeviceManager audioDeviceManager;

public:
  JucePlayer()
  {
    isplaying=false;
    pleasestop=false;
    stoppedproperly=true;
    isinitialized=false;

    num_src_states=0;
    src_states=NULL;

    samplerate=init_jack(audio_jack_callback);

    if(samplerate!=-1){
      isinitialized=true;
      isusingjack=true;
    }else{
      initJuceAudio();
    }

  }


  void initJuceAudio(void){
    const String error (audioDeviceManager.initialise (0, /* number of input channels */
						       2, /* number of output channels */
						       0, /* no XML settings.. */
						       true  /* select default device on failure */));
    
    if (audioDeviceManager.getCurrentAudioDevice()==NULL || error.isNotEmpty())
      {
	AlertWindow::showMessageBox (AlertWindow::WarningIcon,
				     T("Audio Demo"),
				     T("Couldn't open an output device!\n\n") + error);
      }
    else
      {
	isinitialized=true;
	
	// start the IO device pulling its data from our callback..
	audioDeviceManager.setAudioCallback (this);
	
      }
  }

  int getSourceLength(){

  }
  float *getSourceData(int channel,int position){

  }
  double getSourceRate(){

  }
  int getSourceNumChannels(){

  }
  void sourceInit(){
  }
  void sourceCleanup(){
  }

  void insertDataResample(float **outdata,int frames,int num_channels){
    int last_consumed=0;
    for(int ch=0;ch<num_channels;ch++){
      SRC_DATA src_data={
	getSourceData(ch,playpos), outdata[ch],
	getSourceLength()-playpos, frames,
	0,0,
	0,
	samplerate/getSourceRate()
      };
      src_process(src_states[ch],&src_data);

      // This check is probably not necessarry.
      if(ch>0 && last_consumed!=src_data.input_frames)
	fprintf(stderr,"Serious error in playback.\n");
      last_consumed=src_data.input_frames;

      if(ch==num_channels-1){
	playpos+=src_data.input_frames_used;
	if(playpos>=getSourceLength())
	  isplaying=false;
      }
    }
  }

  void insertData(float **outdata,int frames,int num_channels){
    if(frames+playpos <= getSourceLength()){
      for(int ch=0;ch<num_channels;ch++){
	memcpy(outdata[ch],getSourceData(ch,playpos),sizeof(float)*frames);
      }
    }else{
      int len = getSourceLength() - playpos;
      for(int ch=0;ch<num_channels;ch++){
	memcpy(outdata[ch],getSourceData(ch,playpos),sizeof(float)*len);
	zeromem(outdata[ch]+len,sizeof(float)*(frames-len));
      }
    }
    playpos+=frames;
    if(playpos>=getSourceLength())
      isplaying=false;
  }

  void audioDeviceIOCallback(const float ** 	inputChannelData, 
			     int 	totalNumInputChannels, 
			     float ** 	outputChannelData, 
			     int 	totalNumOutputChannels, 
			     int 	numSamples
			     )
  {
    int num_channels=getSourceNumChannels();
    if(num_channels>totalNumOutputChannels)
      num_channels=totalNumOutputChannels;

    if(pleasestop==true)
      isplaying=false;

    if(isplaying==false){
      for(int ch=0;ch<totalNumOutputChannels;ch++){
	zeromem (outputChannelData[ch], sizeof (float) * numSamples);
      }
      return;
    }

    if( (fabs(((double)R) - samplerate)) > 0.5){
      insertDataResample(outputChannelData,numSamples,num_channels);
    }else{
      insertData(outputChannelData,numSamples,num_channels);
    }

    // Play mono files in both loudspeakers.
    if(getSourceNumChannels()==1 && totalNumInputChannels>1)
      memcpy(outputChannelData[1],outputChannelData[0],sizeof(float)*frames);

    for(int ch=getSourceNumChannels();ch<totalNumOutputChannels;ch++){
      zeromem(outputChannelData[ch], sizeof (float) * numSamples);
    }

  }


  void play(){
    if(isinitialized==false)
      return;

    stop();

    sourceInit();

    if(isusingjack==false && audioDeviceManager.getOutputChannels().countNumberOfSetBits()!=getSourceNumChannels()){
      int num_channels=getSourceNumChannels();
      if(num_channels==1)
	num_channels=2;
      outchannels=new BitArray(0);
      for(int lokke=0;lokke<num_channels;lokke++)
	outchannels->setBit(lokke);
      audioDeviceManager.setAudioDevice(
					audioDeviceManager.getAvailableAudioDeviceNames()[0],
					1024,
					R,
					new BitArray(0),
					outchannels,
					false);
    }

    {
      int error;
      for(int i=0;i<num_src_states;i++)
	src_delete(src_states[i]);
      free(src_states);
      
      src_states=(SRC_STATE**)malloc(sizeof(SRC_STATE*)*num_channels);
      
      for(int ch=0;ch<num_channels;ch++)
	src_states[ch]=src_new(SRC_QUALITY,1,&error);
    }

    stoppedproperly=false;
    pleasestop=false;
    playpos=0;
    isplaying=true;
  }

  void stop(){
    if(isinitialized==false)
      return;
    if(stoppedproperly==true)
      return;
    if(isplaying==true){
      pleasestop=true;
      while(isplaying==true)
	Time::waitForMillisecondCounter(Time::getApproximateMillisecondCounter()+100);
    }
    sourceCleanup();
    stoppedproperly=true;
  }
  
  void audioDeviceAboutToStart (double sampleRate, int numSamplesPerBlock)
  {
    samplerate=sampleRate;
    return;
  }
  
  void audioDeviceStopped()
  {
    return;
  }

private:
  double samplerate;
  int playpos;
  bool isplaying;
  bool pleasestop;
  bool stoppedproperly;
  bool isinitialized;
  bool isusingjack;
  bool firstbatch_supplied;
  BitArray *outchannels;
  int num_src_states;
  SRC_STATE **src_states;
};


static JucePlayer *jp=NULL;


void audio_jack_callback (float ** 	inputChannelData, 
			  int 	totalNumInputChannels, 
			  float ** 	outputChannelData, 
			  int 	totalNumOutputChannels, 
			  int 	numSamples
			  )
{
  //printf("Called %d/%d\n",totalNumInputChannels,totalNumOutputChannels);
  jp->audioDeviceIOCallback((const float**)inputChannelData,totalNumInputChannels,outputChannelData,totalNumOutputChannels,numSamples);
}


void juceplay_init(){
  jp=new JucePlayer();
  fprintf(stderr,"Init! ai %p\n",jp);
}

void juceplay_start(){
  jp->play();
}

void juceplay_stop(){
  fprintf(stderr,"stop-ai %p\n",jp);
  jp->stop();
}



/************ JACK part ******************/


static jack_port_t **ports;
typedef jack_default_audio_sample_t sample_t;
static int jack_buffer_size;
static int jack_buffer_size_is_changed_to=0;
static float jack_samplerate;
static int connect_num=0;
static int unreported_overruns=0;
static jack_client_t *client=NULL;
static int channels;
static bool is_initialized=false;




static int findnumports(char **ports){
  int ret=0;
  while(ports && ports[ret]!=NULL)
    ret++;
  return ret;
}


static bool setup_ports(void){
  int ch;
  char **portnames;


  // Find ports to connect to
  {
    portnames=(char **)jack_get_ports(client,NULL,NULL,JackPortIsPhysical|JackPortIsInput);
    channels=JP_MAX(DEFAULT_NUM_CHANNELS,findnumports(portnames));
  }


  // Create my own ports
  {
    ports=calloc(sizeof(jack_port_t *),channels);
    for(ch=0;ch<channels;ch++){
      char temp[500];
      sprintf(temp,"out_%d",ch+1);
      ports[ch]=jack_port_register(
					client,
					strdup(temp),
					JACK_DEFAULT_AUDIO_TYPE,
					JackPortIsOutput,
					0
					);
      if(ports[ch]==NULL){
      fprintf(stderr,"Error. Could not register jack port.\n");
      return false;
      }
    }
  }


  // Connect
  {
    for(ch=0;portnames && portnames[ch]!=NULL && ch<channels;ch++){
      if (
	  jack_connect(
		       client,
		       jack_port_name(ports[ch]),
		       portnames[ch]
		       )
	  )
	{
	  printf ("Warning. Cannot connect to jack output port %d: \"%s\".\n",ch,portnames[ch]);
	}
    }
  }


  return true;
}



static jack_client_t *new_jack_client(char *name){
  jack_client_t *client=jack_client_new(name);

  if(client==NULL){
    for(connect_num=1;connect_num<100;connect_num++){
      char temp[500];
      sprintf(temp,"%s_%d",name,connect_num);
      client=jack_client_new(temp);
      if(client!=NULL)
	return client;
    }
    fprintf(stderr, "\njack server not running? (Unable to create jack client \"%s\")\n",name);
    return NULL;
  }

  return client;
}


static bool start_jack(void){
  client=new_jack_client("mammut");
  if(client==NULL)
    return false;

  jack_buffer_size=jack_get_buffer_size(client);
  jack_samplerate=jack_get_sample_rate(client);

  return true;
}


static void jack_shutdown(void *arg){
  fprintf(stderr,"JACK shut down.\n");
  return;
}


static int process (jack_nframes_t nframes, void *arg){
  void (*audioCallback)(float ** 	inputChannelData, 
			int 	totalNumInputChannels, 
			float ** 	outputChannelData, 
			int 	totalNumOutputChannels, 
			int 	numSamples
			)=arg;
  
  int ch;
  sample_t *out[channels];

  if(is_initialized==false)
    return 1;

  for(ch=0;ch<channels;ch++){
    out[ch]=(sample_t*)jack_port_get_buffer(ports[ch],nframes);
  }

  audioCallback(NULL,0,out,channels,nframes);

  return 0;
}


double init_jack(void (*audioCallback)(float ** 	inputChannelData, 
				       int 	totalNumInputChannels, 
				       float ** 	outputChannelData, 
				       int 	totalNumOutputChannels, 
				       int 	numSamples
				       )){
  if(start_jack()==false)
    return -1.0;

  jack_on_shutdown(client, jack_shutdown, NULL);
  jack_set_process_callback(client, process, audioCallback);

  if(jack_activate(client)){
    fprintf (stderr, "Error. Cannot activate jack client.\n");
    jack_client_close(client);
    return -1.0;
  }
 
  if(setup_ports()==false){
    jack_client_close(client);
    return -1.0;
  }

  is_initialized=true;

  return jack_samplerate;
}

#2

Oops, small bug in the resampling code. I updated the code in the url.


#3

what about modding this, so it will fit as an AudioIODevice directly
(and then shown up in the AudioDeviceSelector) ? instead of creating other standalone classes ? i don’t think it will be difficult !

(anyway i’ve added jack support myself into a Plugin wrapper, so you can write a typical juce plugin, and wrap it to be a jack aware standalone plugin.
http://www.rawmaterialsoftware.com/juceforum/viewtopic.php?t=1044)

/*
  ==============================================================================

   This file is part of the JUCE library - "Jules' Utility Class Extensions"
   Copyright 2004-6 by Raw Material Software ltd.

  ------------------------------------------------------------------------------

   JUCE can be redistributed and/or modified under the terms of the
   GNU General Public License, as published by the Free Software Foundation;
   either version 2 of the License, or (at your option) any later version.

   JUCE is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU General Public License for more details.

   You should have received a copy of the GNU General Public License
   along with JUCE; if not, visit www.gnu.org/licenses or write to the
   Free Software Foundation, Inc., 59 Temple Place, Suite 330,
   Boston, MA 02111-1307 USA

  ------------------------------------------------------------------------------

   If you'd like to release a closed-source product which uses JUCE, commercial
   licenses are also available: visit www.rawmaterialsoftware.com/juce for
   more information.

  ==============================================================================
*/

#ifndef __JUCE_JACKAUDIOFILTERSTREAMER_H__
#define __JUCE_JACKAUDIOFILTERSTREAMER_H__

#include "../../juce_AudioFilterBase.h"
#include <jack/jack.h>
#include <jack/transport.h>

//==============================================================================
/**
    A class that wraps an AudioFilterBase as an JackAudioConnectionKit client,
    so its output can be streamed directly to/from some jack audio and midi
    inputs and outputs.

    To use it, just create an instance of this for your filter, and that's it.

    @code

        DemoFilter* myFilter = new DemoFilter ();
        JackAudioFilterStreamer* myStreamer = new JackAudioFilterStreamer ();

        myStreamer->setFilter (myFilter, true);

    @endcode
*/
class JackAudioFilterStreamer   : public Timer,
                                  public MidiInputCallback,
                                  public AudioFilterBase::FilterNativeCallbacks
{
public:
    //==============================================================================
	JackAudioFilterStreamer ();
	~JackAudioFilterStreamer();

    //==============================================================================
    /**
        Sets on the fly a new filter to be runned by this streamer

        It is useful if you need to change the filter being played at runtime, and
        this will take care of deactivating the old JACK client and create a new
        one based on the filter you passed to it.

        @param filterToUse          the new filter to stream
        @param checkForServer       starts immediately checking if a jack
                                    server is present
    */
    void setFilter (AudioFilterBase* filterToUse, bool checkForServer = true);

    /**
        Calling this after you have created a filter streamer will start checking
        for some JACK server daemon in background. If a server is found and running,
        then your client filter will be activated.

        @see activateClient
    */
    void startCheckForServer ();

    //==============================================================================
    /**
        Try to make this client grab main JACK transport

        If you want to control transport from your filter, you should try to
        grab it by using this function.

        @param conditionalGrab      if true, you can grab the transport ONLY if
                                    no other jack applications have grabbed already.
                                    if false, you are forcing grab of the transport

        @return true if your client grabbed transport correctly
    */
    bool grabTransport (const bool conditionalGrab = true);

    /**
        Release this client grabbing the main JACK transport

        This works only if you already grabbed the transport over all the other
        JACK clients.
    */
    void releaseTransport ();

    /**
        Play the transport

        This will call the server to notify all the other attached clients that
        are listening for transport states. This only works if you already taken
        the transport.
    */
    void playTransport ();

    /**
        Stop main transport

        This will call the server to notify all the other attached clients that
        are listening for transport states. This only works if you already taken
        the transport.
    */
    void stopTransport ();

    /**
        Seek the transport to a specified frame number

        This will call the server to notify all the other attached clients that
        are listening for transport states. This only works if you already taken
        the transport.
    */
    void seekTransportToFrame (const int frameToSeekAt);

    /**
        Set JACK main tempo in beats per seconds
    */
    void setTempo (const double bpmToSet);

    /**
        Set JACK sync timeout in seconds
    */
    void setSyncTimeout (const double secondsTimeout);

    //==============================================================================
    /**
        Create the editor using createEditorInternal of the AudioFilterBase.

        This is needed cause we need to simulate to be a HOST application that
        is trying to create the window for the Editor. And since a lot of
        properties and useful functions are private in the AudioFilterBase, then
        we need to wrap the creation of the editor with this function.

        @see AudioFilterBase::createEditor()
    */
    AudioFilterEditor* createFilterEditor ();

    //==============================================================================
    /** @internal */
    bool activateClient ();
    /** @internal */
    void deactivateClient ();
    /** @internal */
    void closeClient ();
    /** @internal */
    void audioPluginAboutToStart (double sampleRate, int numSamplesPerBlock);
    /** @internal */
    void audioPluginStopped();
    /** @internal */
    void audioPluginIOCallback (int numSamples);
    /** @internal */
    void sampleRateCallback (int newSampleRate);
    /** @internal */
    void blockSizeCallback (int newBlockSize);
    /** @internal */
    void handleIncomingMidiMessage (MidiInput* source, const MidiMessage& message);
    /** @internal */
    bool getCurrentPositionInfo (AudioFilterBase::CurrentPositionInfo& info);
    /** @internal */
    void informHostOfParameterChange (int index, float newValue);
    /** @internal */
    void timerCallback ();

    //==============================================================================
    juce_UseDebuggingNewOperator

private:

    //==============================================================================
    AudioFilterBase* filter;
    AudioFilterEditor* editor;
    bool isPlaying;
    double sampleRate;
    int blockSize;
    MidiMessageCollector midiCollector;

    float* outChans [128];
    float* inChans [128];

    MidiInput* midiInput;
    MidiOutput* midiOutput;

    jack_client_t *client;
    VoidArray inputPorts;
    VoidArray outputPorts;

    double beatsPerMinute;
    double ticksPerBeat;
    float beatsPerBar;     // numerator
    float beatType;        // denominator

    bool holdTransport;
    bool timeHasChanged; // volatile ?
};

#endif
/*
  ==============================================================================

   This file is part of the JUCE library - "Jules' Utility Class Extensions"
   Copyright 2004-6 by Raw Material Software ltd.

  ------------------------------------------------------------------------------

   JUCE can be redistributed and/or modified under the terms of the
   GNU General Public License, as published by the Free Software Foundation;
   either version 2 of the License, or (at your option) any later version.

   JUCE is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU General Public License for more details.

   You should have received a copy of the GNU General Public License
   along with JUCE; if not, visit www.gnu.org/licenses or write to the
   Free Software Foundation, Inc., 59 Temple Place, Suite 330,
   Boston, MA 02111-1307 USA

  ------------------------------------------------------------------------------

   If you'd like to release a closed-source product which uses JUCE, commercial
   licenses are also available: visit www.rawmaterialsoftware.com/juce for
   more information.

  ==============================================================================
*/

#include "juce_JackAudioFilterStreamer.h"
#include "JucePluginCharacteristics.h"

//==============================================================================
static void juce_internalJackThreadInitCallback (void *arg);
static void juce_internalJackOnShutdownCallback (void *arg);
static int juce_internalJackBufferSizeCallback (jack_nframes_t nframes, void *arg);
static int juce_internalJackSampleRateCallback (jack_nframes_t nframes, void *arg);
static void juce_internalJackPortRegistrationCallback (jack_port_id_t port, int, void *arg);
static void juce_internalJackFreewheelCallback (int starting, void *arg);
static int juce_internalJackGraphOrderCallback (void *arg);
static int juce_internalJackProcessCallback (jack_nframes_t nframes, void* arg);
static int juce_internalJackXRunCallback (void *arg);
static int juce_internalJackSyncCallback (jack_transport_state_t state, jack_position_t *pos, void *arg);
static void juce_internalJackTimebaseCallback (jack_transport_state_t state,
                                               jack_nframes_t nframes,
                                               jack_position_t *pos,
                                               int new_pos,
                                               void *arg);




//==============================================================================
JackAudioFilterStreamer::JackAudioFilterStreamer ()
    : filter (0),
      editor (0),
      isPlaying (false),
      sampleRate (0),
      blockSize (0),
      midiInput (0),
      midiOutput (0),
      client (0),
      beatsPerMinute (120.0),
      holdTransport (false),
      timeHasChanged (false)
{
}

JackAudioFilterStreamer::~JackAudioFilterStreamer()
{
    if (filter && editor != 0)
        filter->editorBeingDeleted (editor);

    audioPluginStopped();
    closeClient ();
}

//==============================================================================
void JackAudioFilterStreamer::setFilter (AudioFilterBase* filterToUse, bool checkForServer)
{
    audioPluginStopped ();
    deactivateClient ();

    filter = filterToUse;
    if (filter)
    {
        filter->initialiseInternal (this);

        if (checkForServer)
            startCheckForServer ();
    }
}

//==============================================================================
bool JackAudioFilterStreamer::activateClient ()
{
    jassert (filter);  // you should have already attached a filter,
                       // if not, how would this streamer works ?
    sampleRate = 0;
    blockSize = 0;

    // create the jack client
    jack_status_t status;
    client = jack_client_open (JucePlugin_Name, JackNoStartServer, &status);
    if (client == 0)
        return false;

    DBG ("client opened");

    AudioFilterBase::FilterInfo info = filter->getFilterInfo();

    // setup callbacks
    jack_set_thread_init_callback (client, juce_internalJackThreadInitCallback, this);
    jack_set_buffer_size_callback (client, juce_internalJackBufferSizeCallback, this);
    jack_set_sample_rate_callback (client, juce_internalJackSampleRateCallback, this);
    jack_set_port_registration_callback (client, juce_internalJackPortRegistrationCallback, this);
    jack_set_graph_order_callback (client, juce_internalJackGraphOrderCallback, this);
    jack_set_freewheel_callback (client, juce_internalJackFreewheelCallback, this);
    jack_set_process_callback (client, juce_internalJackProcessCallback, this);
    jack_set_xrun_callback (client, juce_internalJackXRunCallback, this);
    jack_on_shutdown (client, juce_internalJackOnShutdownCallback, this);
    jack_set_sync_callback (client, juce_internalJackSyncCallback, this);

#ifdef JUCE_DEBUG
//    jack_set_error_function (default_jack_error_callback);
#endif

    jassert (info.numInputChannels <= 128);   // your filter have more input channels
                                              // than the maximum allowed to be handle by this streamer !
    jassert (info.numOutputChannels <= 128);  // your filter have more input channels
                                              // than the maximum allowed to be handle by this streamer !

    // register input ports
    for (int i = 0; i < info.numInputChannels; i++)
    {
        String inputName;
        inputName << "in_" << (i+1);

        jack_port_t* input =
                jack_port_register (client, (const char*) inputName, JACK_DEFAULT_AUDIO_TYPE, JackPortIsInput, 0);

        inputPorts.add (input);
    }

    // register output ports
    for (int i = 0; i < info.numOutputChannels; i++)
    {
        String outputName;
        outputName << "out_" << (i+1);

        jack_port_t* output =
                jack_port_register (client, (const char*) outputName, JACK_DEFAULT_AUDIO_TYPE, JackPortIsOutput, 0);

        jack_port_set_latency (output, info.latencySamples);

        outputPorts.add (output);
    }

    // clear previouslyu created midi devices
    if (midiInput)
        midiInput->stop ();
    deleteAndZero (midiInput);
    deleteAndZero (midiOutput);

    // create midi devices
    if (info.wantsMidiInput) {
        midiInput = MidiInput::openDevice (MidiInput::getDefaultDeviceIndex(), this);
        midiInput->start ();
    }

    if (info.producesMidiOutput)
        midiOutput = MidiOutput::openDevice (MidiOutput::getDefaultDeviceIndex());

    // reset midi collector before we start
    midiCollector.reset (jack_get_sample_rate (client));

    // update samplerate and block size before we just activate the client
    sampleRateCallback (jack_get_sample_rate (client));
    blockSizeCallback (jack_get_buffer_size (client));

    // activate jack client
    jack_activate (client);

    return true;
}

void JackAudioFilterStreamer::deactivateClient ()
{
    if (client)
    {
        jack_deactivate (client);
        client = 0;

        DBG ("client deactivated");
    }

    if (midiInput)
        midiInput->stop ();

    deleteAndZero (midiInput);
    deleteAndZero (midiOutput);
}

void JackAudioFilterStreamer::closeClient ()
{
    if (client)
    {
        jack_deactivate (client);
        jack_client_close (client);
        client = 0;

        DBG ("client closed");
    }

    if (midiInput)
        midiInput->stop ();

    deleteAndZero (midiInput);
    deleteAndZero (midiOutput);
}

//==============================================================================
void JackAudioFilterStreamer::audioPluginIOCallback (int numSamples)
{
    jassert (filter);  // you should have already attached a filter,
                       // if not, how would this streamer works ?

    if (numSamples < 0)
        return;

    MidiBuffer midiBuffer;
    midiCollector.removeNextBlockOfMessages (midiBuffer, numSamples);

    int i;
    const int numOutsWanted = filter->getFilterInfo().numOutputChannels;
    const int numInsWanted = filter->getFilterInfo().numInputChannels;

    for (i = 0; i < numInsWanted; ++i)
    {
        jack_default_audio_sample_t *in =
            (jack_default_audio_sample_t *) jack_port_get_buffer (
                                                    (jack_port_t*) inputPorts.getUnchecked(i), numSamples);
        if (in != 0)
            inChans [i] = in;
    }

    for (i = 0; i < numOutsWanted; ++i)
    {
        jack_default_audio_sample_t *out =
            (jack_default_audio_sample_t *) jack_port_get_buffer (
                                                    (jack_port_t*) outputPorts.getUnchecked(i), numSamples);
      if (out != 0)
            outChans [i] = out;
    }

    AudioSampleBuffer input (inChans, numInsWanted, numSamples);
    AudioSampleBuffer output (outChans, numOutsWanted, numSamples);

    {
        const ScopedLock sl (filter->getCallbackLock());

        if (filter->suspended)
            output.clear();
        else
        	filter->processBlock (input, output, false, midiBuffer);
    }

    if (midiOutput)
    {
        int samplePos = 0;
        MidiMessage message (0,0);
        MidiBuffer::Iterator eventIterator (midiBuffer);
        while (eventIterator.getNextEvent (message, samplePos))
            midiOutput->sendMessageNow (message);
    }
}

void JackAudioFilterStreamer::sampleRateCallback (int newSampleRate)
{
  sampleRate = (double) newSampleRate;

  audioPluginStopped ();

  if (sampleRate != 0 && blockSize != 0)
    audioPluginAboutToStart (sampleRate, blockSize);
}

void JackAudioFilterStreamer::blockSizeCallback (int newBlockSize)
{
  blockSize = newBlockSize;

  audioPluginStopped ();

  if (sampleRate != 0 && blockSize != 0)
    audioPluginAboutToStart (sampleRate, blockSize);
}

void JackAudioFilterStreamer::audioPluginAboutToStart (double sampleRate_,
                                                       int numSamplesPerBlock)
{
    sampleRate = sampleRate_;

    if (filter && ! isPlaying)
    {
        isPlaying = true;

        midiCollector.reset (sampleRate);

        filter->prepareToPlay (sampleRate, numSamplesPerBlock);

        DBG ("plugin starting");
    }
}

void JackAudioFilterStreamer::audioPluginStopped()
{
    if (filter && isPlaying)
    {
        isPlaying = false;

        filter->releaseResources();

        midiCollector.reset (sampleRate);

        DBG ("plugin stopping");
    }
}

//==============================================================================
void JackAudioFilterStreamer::handleIncomingMidiMessage (MidiInput* source, const MidiMessage& message)
{
    if (filter && filter->getFilterInfo().wantsMidiInput)
        midiCollector.addMessageToQueue (message);
}

bool JackAudioFilterStreamer::getCurrentPositionInfo (AudioFilterBase::CurrentPositionInfo& info)
{
    jack_position_t pos;
    jack_transport_state_t state = jack_transport_query (client, &pos);
/*
    jack_nframes_t frame_rate;
    jack_time_t    usecs;
    jack_transport_bits_t  valid;
    jack_transport_state_t transport_state;
    jack_nframes_t         frame;
    jack_nframes_t         loop_start;
    jack_nframes_t         loop_end;

    long           smpte_offset;
    float          smpte_frame_rate;
    int            bar;
    int            beat;
    int            tick;
    double         bar_start_tick;

    float          beats_per_bar;
    float          beat_type;
    double         ticks_per_beat;
    double         beats_per_minute;
*/

    if ((pos.valid & JACK_POSITION_MASK) &&
         pos.beat_type > 0 && pos.beats_per_minute > 0)
    {
        info.bpm = pos.beats_per_minute;
        info.timeSigNumerator = roundFloatToInt (pos.beats_per_bar);
        info.timeSigDenominator = roundFloatToInt (pos.beat_type);
        info.timeInSeconds = pos.frame_time;
        info.editOriginTime = 1; // @XXX;
        info.ppqPosition = 1; // @XXX;
        info.ppqPositionOfLastBarStart = 1; // @XXX;
        info.frameRate = AudioFilterBase::CurrentPositionInfo::fps24;
        info.isPlaying = (state & JackTransportRolling);
        info.isRecording = false;
        return true;
    }
    return false;
}

void JackAudioFilterStreamer::informHostOfParameterChange (int index, float newValue)
{
    jassert (filter);  // you should have already attached a filter,
                       // if not, how would this streamer works ?

    filter->setParameter (index, newValue);
}

//==============================================================================
void JackAudioFilterStreamer::startCheckForServer ()
{
    startTimer (1000);
}

void JackAudioFilterStreamer::timerCallback ()
{
    DBG ("client tries to connect to server");
    if (activateClient())
    {
        stopTimer();
    }
}

//==============================================================================
bool JackAudioFilterStreamer::grabTransport (const bool conditionalGrab)
{
    if (client)
    {
        return (holdTransport = (jack_set_timebase_callback (client,
                                                             (conditionalGrab ? 1 : 0),
                                                             juce_internalJackTimebaseCallback,
                                                             this) == 0));
    }
    else
    {
        return false;
    }
}

void JackAudioFilterStreamer::releaseTransport ()
{
  if (client && holdTransport)
    jack_release_timebase(client);
}

//==============================================================================
void JackAudioFilterStreamer::playTransport ()
{
  if (client && holdTransport)
    jack_transport_start(client);
}

void JackAudioFilterStreamer::stopTransport ()
{
  if (client && holdTransport)
    jack_transport_stop(client);
}

void JackAudioFilterStreamer::seekTransportToFrame (const int frameToSeekAt)
{
  if (client && holdTransport)
  {
    jack_nframes_t frameNumber = 0;

    if (frameToSeekAt >= 0)
      frameNumber = frameToSeekAt;

    jack_transport_locate (client, frameNumber);
  }
}

//==============================================================================
void JackAudioFilterStreamer::setTempo (const double bpmToSet)
{
  if (bpmToSet > 0.0)
  {
    if (beatsPerMinute != bpmToSet)
    {
      beatsPerMinute = bpmToSet;
      timeHasChanged = true;
    }
  }
}

void JackAudioFilterStreamer::setSyncTimeout (const double secondsTimeout)
{
  if (client && holdTransport)
  {
    double timeout = 2.0;

    if (secondsTimeout > 0)
      timeout = secondsTimeout;

    jack_set_sync_timeout (client, (jack_time_t) (timeout*1000000));
  }
}

//==============================================================================
AudioFilterEditor* JackAudioFilterStreamer::createFilterEditor ()
{
    jassert (editor == 0); // can't actually call this twice !

    editor = (filter != 0) ? filter->createEditorInternal() : 0;
    return editor;
}

//==============================================================================
static void juce_internalJackThreadInitCallback (void *arg)
{
    DBG ("server starting");
}

static void juce_internalJackOnShutdownCallback (void* arg)
{
    DBG ("server shutdown");
    JackAudioFilterStreamer* filterStreamer = (JackAudioFilterStreamer*) arg;
    filterStreamer->audioPluginStopped ();
    filterStreamer->deactivateClient ();
    filterStreamer->startCheckForServer ();
}

static int juce_internalJackBufferSizeCallback (jack_nframes_t nframes, void* arg)
{
    JackAudioFilterStreamer* filterStreamer = (JackAudioFilterStreamer*) arg;
    filterStreamer->blockSizeCallback (nframes);
    DBG ("server blocksize changed");
    return 0;
}

static int juce_internalJackSampleRateCallback (jack_nframes_t nframes, void* arg)
{
    JackAudioFilterStreamer* filterStreamer = (JackAudioFilterStreamer*) arg;
    filterStreamer->sampleRateCallback (nframes);
    DBG ("server samplerate changed");
    return 0;
}

static void juce_internalJackPortRegistrationCallback (jack_port_id_t port, int, void* )
{
//  JackAudioFilterStreamer* filterStreamer = (JackAudioFilterStreamer*) arg;
    DBG ("server port XXX registered");
}

static void juce_internalJackFreewheelCallback (int starting, void *arg)
{
    DBG ("server freewheel changed");
}

static int juce_internalJackGraphOrderCallback (void *arg)
{
//    JackAudioFilterStreamer* filterStreamer = (JackAudioFilterStreamer*) arg;
//    filterStreamer->audioPluginStopped ();
//    printf ("graph reorder\n");
    return 0;
}

static int juce_internalJackProcessCallback (jack_nframes_t nframes, void* arg)
{
  JackAudioFilterStreamer* filterStreamer = (JackAudioFilterStreamer*) arg;
  filterStreamer->audioPluginIOCallback (nframes);
  return 0;
}

static int  juce_internalJackXRunCallback (void *arg)
{
    DBG ("server catched underrun");
    return 0;
}

static int juce_internalJackSyncCallback (jack_transport_state_t state, jack_position_t *pos, void *arg)
{
    /*
    printf ("sync: %d:%d %d %3.1f/%3.1f %f %f\n",
            pos->beat, pos->bar, pos->tick, pos->beats_per_bar, pos->beat_type, pos->ticks_per_beat, pos->beats_per_minute );
    */
    return 0;
}

static float time_beats_per_bar = 4.0;
static float time_beat_type = 4.0;
static double time_ticks_per_beat = 1920.0;
static double time_beats_per_minute = 120.0;
volatile int time_reset = 0;

static void juce_internalJackTimebaseCallback (jack_transport_state_t state,
                                               jack_nframes_t nframes,
                                               jack_position_t *pos,
                                               int new_pos,
                                               void *arg)
{
  double min;         // minutes since frame 0
  long abs_tick;      // ticks since frame 0
  long abs_beat;      // beats since frame 0

//  JackAudioFilterStreamer* filterStreamer = (JackAudioFilterStreamer*) arg;

  if (new_pos || time_reset)
  {
      pos->valid = JackPositionBBT;
      pos->beats_per_bar = time_beats_per_bar;
      pos->beat_type = time_beat_type;
      pos->ticks_per_beat = time_ticks_per_beat;
      pos->beats_per_minute = time_beats_per_minute;

//      filterStreamer->timeHasChanged = false; // time change complete

      // Compute BBT info from frame number.  This is relatively
      // simple here, but would become complex if we supported tempo
      // or time signature changes at specific locations in the
      // transport timeline.

      min = pos->frame / ((double) pos->frame_rate * 60.0);
      abs_tick = roundDoubleToInt (min * pos->beats_per_minute * pos->ticks_per_beat);
      abs_beat = roundDoubleToInt (abs_tick / pos->ticks_per_beat);

      pos->bar = roundFloatToInt (abs_beat / pos->beats_per_bar);
      pos->beat = roundFloatToInt (abs_beat - (pos->bar * pos->beats_per_bar) + 1);
      pos->tick = roundFloatToInt (abs_tick - (abs_beat * pos->ticks_per_beat));
      pos->bar_start_tick = roundFloatToInt (pos->bar * pos->beats_per_bar * pos->ticks_per_beat);
      pos->bar++;     // adjust start to bar 1

#if JUCE_DEBUG
//        printf ("%f %f %f %f", pos->frame, pos->bar, pos->beat, pos->tick);
#endif

  }
  else
  {
      // Compute BBT info based on previous period.
      pos->tick += roundDoubleToInt (nframes * pos->ticks_per_beat * pos->beats_per_minute / (pos->frame_rate * 60));

      while (pos->tick >= pos->ticks_per_beat)
      {
          pos->tick -= roundDoubleToInt (pos->ticks_per_beat);
          if (++pos->beat > pos->beats_per_bar)
          {
              pos->beat = 1;
              ++pos->bar;
              pos->bar_start_tick += pos->beats_per_bar * pos->ticks_per_beat;
          }
      }
  }
}

now this topic is perfect ! long live jack audio !


#4

[quote=“kraken”]what about modding this, so it will fit as an AudioIODevice directly
(and then shown up in the AudioDeviceSelector) ? instead of creating other standalone classes ? i don’t think it will be difficult !
[/quote]

Probably not very difficult, but I’m working on a program which needs to be finished quite soon, so I just made something quick which just works. (Besides, I’m more of a lisper, and C++ is a bit frustrating for me.)

Yes, I saw that one, but you never posted any code. :wink:


#5

yep, you right… i’ve sent Jules the code to be add in the packages, my mistake not think that Jules is always busy…


#6