Audio problems...

For Linux specific issues

Postby jules » Tue Dec 09, 2008 10:56 am

I did originally use plughw, and there were endless problems with it glitching, not opening, etc etc, but maybe as a second try it'd be good.

What I really need to do is get round to adding jack support. Been meaning to sort that out for ages now..
User avatar
jules
Fearless Leader
 
Posts: 17189
Joined: Mon Sep 06, 2004 9:03 am
Location: London, UK

Postby lumbergh » Tue Dec 09, 2008 12:45 pm

Strange, I haven't had any such problem, but who knows what kind of weird and inefficient converter alsa may decide to use.

Maybe just hw is best then, but to avoid 16 bit output, it is probably a good idea to add SND_PCM_FORMAT_S24_*E again.

I do get sound in JUCE Demo on Gentoo AMD64 now anyway.
But for some reason the default device does not work, (the infamous: Invalid argument), I have to select it from Audio Settings first to make it work.
lumbergh
JUCE Weenie
 
Posts: 8
Joined: Sat Dec 06, 2008 2:13 pm
Location: Sweden

Postby zamrate » Wed Jan 14, 2009 11:16 am

Today I made my first Linux experiences (Ubuntu 8.10). I built JUCE 1.46, but I also get the "Invalid Argument" message. I tried the #define DD "default" trick, it does not help. I also tried the other things, without success. My card is a Delta1010LT with an ICE1712 chip. [EDIT: Also my U46DJ USB soundcard doesn't work].

Questions:
- Does the tip provide some kind of audio support for this card, via ALSA? [EDIT: No it doesn't. Still the same. It's no ALSA, because other apps work with ALSA.]
- Since I'm a LINUX newbie, and if anybody got some similar problems with a similar card, has anybody got some advice what I should do next?
User avatar
zamrate
JUCE UberWeenie
 
Posts: 1081
Joined: Mon Sep 24, 2007 5:33 pm

Postby zamrate » Sun Jan 18, 2009 8:03 pm

Well, well. I figured out how to debug on Linux, so I debugged the ALSA code and found out where JUCE exactly fails with the "Invalid Argument" message.

It's the line snd_pcm_hw_params_set_channels (handle, hwParams, numChannels) that fails! numChannels is 2, but my card has 10 outs and 12 ins, and I strongly suspect that it would work if numChannels was set to the real number of outs or ins.

Hope this helps, looking forward to see ALSA work in JUCE.
User avatar
zamrate
JUCE UberWeenie
 
Posts: 1081
Joined: Mon Sep 24, 2007 5:33 pm

Postby zamrate » Sun Jan 18, 2009 9:08 pm

Bingo. I was right. I modified the JUCE Audio Demo in Line 452 to this:

Code: Select all
const String error (audioDeviceManager.initialise (12, /* number of input channels */

                                                           10, /* number of output channels */

                                                           0, /* no XML settings.. */

                                                           true  /* select default device on failure */))


and now my soundcard works!

It seems that this is a JUCE bug. In fact the output of AudioIODevice::testDevice() clearly prints that my soundcard has a minimum of 10 outs and a maximum of 10 outs, as well as a minimum of 12 inputs and a maximum of 12 inputs. So passing 1 as number of inputs and 2 as number of outputs to ALSA can only fail !?
By the way, if the failed() funciton would print the Line number where it failed or the function name that failed would also help a little bit to find out such bugs :roll:
User avatar
zamrate
JUCE UberWeenie
 
Posts: 1081
Joined: Mon Sep 24, 2007 5:33 pm

Postby kraken » Mon Jan 19, 2009 9:22 am

use my version of the linux platform specific alsa code:

http://www.anticore.org/tmp/juce_linux_Audio.cpp

i can add also the __LINE__ macro when you call the function.

cheers !
User avatar
kraken
JUCE UberWeenie
 
Posts: 1063
Joined: Wed Feb 09, 2005 10:31 am
Location: Venice, Italy

Postby zamrate » Mon Jan 19, 2009 9:26 am

have already fixed the code for my purposes (just always uses all IO channels of the soundcard, worked well for me), cheers kraken.
User avatar
zamrate
JUCE UberWeenie
 
Posts: 1081
Joined: Mon Sep 24, 2007 5:33 pm

Postby jules » Mon Feb 02, 2009 7:25 pm

So you're suggesting that doing something like this would sort it out?

Code: Select all
            if (! outputDevice->setParameters ((unsigned int) sampleRate,
                                               jmax (minChansOut, currentOutputChans.getHighestBit() + 1),
                                               bufferSize))
            {


?

(and obviously the same for the number of input channels)
User avatar
jules
Fearless Leader
 
Posts: 17189
Joined: Mon Sep 06, 2004 9:03 am
Location: London, UK

Postby zamrate » Tue Feb 03, 2009 9:52 am

I'd rather write:

Code: Select all
if (! outputDevice->setParameters ((unsigned int) sampleRate,
                                               jlimit (minChansOut, maxChansOut, currentOutputChans.getHighestBit() + 1),
                                               bufferSize))
            {


It's safer I guess. Yes, that should work. At least on my PC this was the problem that made it impossible to open the ALSA device.
User avatar
zamrate
JUCE UberWeenie
 
Posts: 1081
Joined: Mon Sep 24, 2007 5:33 pm

Postby jules » Tue Feb 03, 2009 12:50 pm

Ok, I'll put that in and see if it works.
User avatar
jules
Fearless Leader
 
Posts: 17189
Joined: Mon Sep 06, 2004 9:03 am
Location: London, UK

Postby jpo » Thu Apr 23, 2009 6:37 pm

kraken wrote:here it is the juce_linux_JackAudio.cpp
(..)
hope it will be included in the main trunk :)
(EDIT)


I too would like to be able to use jack as a backend with juce. Kraken, may I ask you what is the licence of that file ? You have kept the original "GPL licence + rawmaterialsoftware copyright" header, does that mean that I can use this file in a closed-source project since I have a commercial licence for Juce ? Or do you license it under GPL with your own copyright, in which case I will have to rewrite it :-(
jpo
JUCE UberWeenie
 
Posts: 336
Joined: Thu Mar 20, 2008 2:45 pm

Postby kraken » Fri Apr 24, 2009 8:57 am

well actually that was meant to be included in the main JUCE trunk, so it should actually inherit the JUCE licensing format.

Obviously, since i'm so generous by opening that stuff for you, any improvements you make could be really appreciated... but it's up to you !

Feel free to use and abuse, i don't see any licensing problem in that.
Image
User avatar
kraken
JUCE UberWeenie
 
Posts: 1063
Joined: Wed Feb 09, 2005 10:31 am
Location: Venice, Italy

Postby jpo » Fri Apr 24, 2009 5:15 pm

Thanks a lot for the clarification ! I'm looking at the code right now, and will have a few fixes to contribute, but for now I'm wondering what is the "best" way to handle connections to jack ports:

- the approach you have taken, which is to scan all input ports and outports currently reported by jack , and present them as "output devices" and "input devices". So if you have a sequencer running with 8 inputs port, and a soundcard with 4 outputs, the juce application will see an "output device" with 8 channels for the sequencer, and an output device with 4 channels for the soundcard. Etc.

- the "plugin" approach that would be to create as many input and output port that your application handles (2 output port if you just have stereo output, or 8 output ports etc). The advantage of that approach is that you always get the "best" number of channels for your application and the user can route them to anything. The juce application always sees only 1 device named "jack" which has as many channels than the juce application requested (JucePlugin_MaxOutputChannels). But the user has to manage the port connections himself, for example using the patchbay of qjackctl

What do you think ? Right now I'm wondering if I should not rewrite it using the second approach (which would also simplify stuff since the code would not handle port connections anymore)
jpo
JUCE UberWeenie
 
Posts: 336
Joined: Thu Mar 20, 2008 2:45 pm

Postby kraken » Sat Apr 25, 2009 12:18 pm

i've done both as you can see here:

- the "plugin" approach (as you called it). i open as many inputs and outputs the juce plugin needs and don't care about connections: this is better for a standalone wrapper approach.

http://code.google.com/p/juced/source/b ... Streamer.h
http://code.google.com/p/juced/source/b ... reamer.cpp

- the "audio device", i scan every device and let you connect directly your application from inside the AudioDeviceManager (remembering the connections). this way you can connect to a single device only at a time, but can't be done otherwise inside the AudioDeviceManager gui (you still can use qjackctl outside juce to to other connections)

http://code.google.com/p/juced/source/b ... kAudio.cpp

i prefer the first approach, but still i want the user to be able to select to which default hardware output device they want without having to use an external jack-control application to do i/o wiring, so i ended up with the second and i'm pretty happy with it.

it only needs a bit of improvement here and there and some more testing of course.
Image
User avatar
kraken
JUCE UberWeenie
 
Posts: 1063
Joined: Wed Feb 09, 2005 10:31 am
Location: Venice, Italy

Postby jpo » Sun Apr 26, 2009 10:59 pm

oh I missed you audiofilterstream ! However I think it does not fit with my application which expects au AudioDeviceManager etc. Instead I have taken your JackAudioIODevice class and stripped all the ports connection stuff to keep the bare minimum (btw in the shutdown callback, you should set device->client = 0 before calling the close() method, or the application dies with a SIGPIPE):

Code: Select all
#include "jack_device.hh"

static const char* defaultJackAudioDeviceName = "jackd";



//==============================================================================
class JackAudioIODevice   : public AudioIODevice
{
    JackClientConfiguration config;
public:
    JackAudioIODevice (JackClientConfiguration &config_)
        : AudioIODevice (defaultJackAudioDeviceName, T("JACK")),
          config(config_),
          isOpen_ (false),
          isStarted (false),
          callback (0),
          client (0)
    {
        for (int i=0; i < config.inputChannels.size(); ++i) {
            if (config.inputChannels[i].length() == 0) config.inputChannels.set(i, T("in_")+String(i));
        }
        for (int i = 0; i < config.outputChannels.size(); i++) {
            if (config.outputChannels[i].length() == 0) config.outputChannels.set(i, T("out_")+String(i));
        }

        jack_status_t status;
        client = jack_client_open (config.clientName.toUTF8(), JackNoStartServer, &status);
        if (client == 0)
        {
            if ((status & JackServerFailed) || (status & JackServerError))
                printf ("Unable to connect to JACK server\n");
            if ((status & JackVersionError))
                printf ("Client's protocol version does not match\n");
            if ((status & JackInvalidOption))
                printf ("The operation contained an invalid or unsupported option\n");
            if ((status & JackNameNotUnique))
                printf ("The desired client name was not unique\n");
            if ((status & JackNoSuchClient))
                printf ("Requested client does not exist\n");
            if ((status & JackInitFailure))
                printf ("Unable to initialize client\n");
        }
        else
        {
            jack_set_error_function (JackAudioIODevice::errorCallback);

            for (int i=0; i < config.inputChannels.size(); ++i) {
                jack_port_t* input =
                    jack_port_register (client, config.inputChannels[i].toUTF8(), JACK_DEFAULT_AUDIO_TYPE, JackPortIsInput, 0);
                inputPorts.add (input);
            }

            for (int i = 0; i < config.outputChannels.size(); i++) {
                jack_port_t* output =
                    jack_port_register (client, config.outputChannels[i].toUTF8(), JACK_DEFAULT_AUDIO_TYPE, JackPortIsOutput, 0);
                outputPorts.add (output);
           }
        }
        inChans  = (float**)juce_malloc(config.inputChannels.size() *sizeof(float*));
        outChans = (float**)juce_malloc(config.outputChannels.size()*sizeof(float*));
    }

    ~JackAudioIODevice()
    {
        if (client)
        {
            close ();

            jack_client_close (client);
            client = 0;
        }
        juce_free(inChans);
        juce_free(outChans);
    }

    const StringArray getOutputChannelNames() { return config.outputChannels; }

    const StringArray getInputChannelNames() { return config.inputChannels; }

    int getNumSampleRates()
    {
        return client ? 1 : 0;
    }

    double getSampleRate (int /*index*/)
    {
        return client ? jack_get_sample_rate (client) : 0;
    }

    int getNumBufferSizesAvailable()
    {
        return client ? 1 : 0;
    }

    int getBufferSizeSamples (int /*index*/)
    {
        return client ? jack_get_buffer_size (client) : 0;
    }

    int getDefaultBufferSize()
    {
        return client ? jack_get_buffer_size (client) : 0;
    }

    const String open (const BitArray& /*inputChannels*/,
                       const BitArray& /*outputChannels*/,
                       double /*sampleRate*/,
                       int /*bufferSizeSamples*/)
    {
        if (! client)
        {
            return T("Jack server is not running");
        }

        close();

        // activate client !       
        jack_set_process_callback (client, JackAudioIODevice::processCallback, this);
        jack_on_shutdown (client, JackAudioIODevice::shutdownCallback, this);

        jack_activate (client);
        isOpen_ = true;

        return String::empty;
    }

    void close()
    {
        stop();

        if (client)
        {
            jack_deactivate (client);

            jack_set_process_callback (client, JackAudioIODevice::processCallback, 0);
            jack_on_shutdown (client, JackAudioIODevice::shutdownCallback, 0);
        }

        isOpen_ = false;
    }

    bool isOpen()
    {
        return isOpen_;
    }

    int getCurrentBufferSizeSamples()
    {
        return getBufferSizeSamples (0);
    }

    double getCurrentSampleRate()
    {
        return getSampleRate (0);
    }

    int getCurrentBitDepth()
    {
        return 32;
    }

    const BitArray getActiveOutputChannels() const
    {
        BitArray outputBits;
        outputBits.setRange(0, outputPorts.size(), true);
        return outputBits;
    }

    const BitArray getActiveInputChannels() const
    {
        BitArray inputBits;
        inputBits.setRange(0, inputPorts.size(), true);
        return inputBits;
    }

    int getOutputLatencyInSamples()
    {
        int latency = 0;
       
        for (int i = 0; i < outputPorts.size(); i++)
            latency = jmax (latency, (int) jack_port_get_total_latency (client, (jack_port_t*) outputPorts [i]));
   
        return latency;
    }

    int getInputLatencyInSamples()
    {
        int latency = 0;
       
        for (int i = 0; i < inputPorts.size(); i++)
            latency = jmax (latency, (int) jack_port_get_total_latency (client, (jack_port_t*) inputPorts [i]));
   
        return latency;
    }

    void start (AudioIODeviceCallback* callback_)
    {
        if (! isOpen_)
            callback_ = 0;

        callback = callback_;

        if (callback != 0)
            callback->audioDeviceAboutToStart (this);

        isStarted = (callback != 0);
    }

    void process (int numSamples)
    {
        int i, numActiveInChans = 0, numActiveOutChans = 0;

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

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

        if (callback != 0)
        {
            callback->audioDeviceIOCallback ((const float**) inChans,
                                             inputPorts.size(),
                                             outChans,
                                             outputPorts.size(),
                                             numSamples);
        }
        else
        {
            for (i = 0; i < outputPorts.size(); ++i)
                zeromem (outChans[i], sizeof (float) * numSamples);
        }
    }

    void stop()
    {
        AudioIODeviceCallback* const oldCallback = callback;

        start (0);

        if (oldCallback != 0)
            oldCallback->audioDeviceStopped();
    }

    bool isPlaying()
    {
        return isStarted;
    }

    const String getLastError()
    {
        return String::empty;
    }

    String inputId, outputId;

private:

    static void threadInitCallback (void* /*callbackArgument*/)
    {
    }
   
    static void shutdownCallback (void* callbackArgument)
    {
        JackAudioIODevice* device = (JackAudioIODevice*) callbackArgument;
   
        if (device)
        {
            device->client = 0;
            device->close ();
        }
    }
   
    static int processCallback (jack_nframes_t nframes, void* callbackArgument)
    {
        JackAudioIODevice* device = (JackAudioIODevice*) callbackArgument;

        if (device)
            device->process (nframes);

        return 0;
    }
   
    static void errorCallback (const char *msg)
    {
        fprintf (stderr, "Jack error: %s\n", msg);
    }

    bool isOpen_, isStarted;

    AudioIODeviceCallback* callback;
   
    float** inChans;
    float** outChans;

    jack_client_t *client;
    VoidArray inputPorts;
    VoidArray outputPorts;
};


//==============================================================================
class JackAudioIODeviceType  : public AudioIODeviceType
{
public:
    //==============================================================================
    JackAudioIODeviceType()
        : AudioIODeviceType(T("JACK"))
    {
    }

    ~JackAudioIODeviceType()
    {
    }

   

    //==============================================================================
    void scanForDevices()
    {
    }

    const StringArray getDeviceNames (const bool /*wantInputNames*/) const
    {
        StringArray s;
        s.add(defaultJackAudioDeviceName);
        return s;
    }

    int getDefaultDeviceIndex (const bool /*forInput*/) const
    {
        return 0;
    }

    bool hasSeparateInputsAndOutputs() const    { return false; }

    int getIndexOfDevice (AudioIODevice* device, const bool /*asInput*/) const
    {
        JackAudioIODevice* const d = dynamic_cast <JackAudioIODevice*> (device);
        return (d == 0) ? -1 : 0;
    }

    AudioIODevice* createDevice (const String& /*outputDeviceName*/,
                                 const String& /*inputDeviceName*/)
    {
        JackClientConfiguration config;
        getJackClientConfiguration(config);
        return new JackAudioIODevice(config);
        return 0;
    }

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

private:
    JackAudioIODeviceType (const JackAudioIODeviceType&);
    const JackAudioIODeviceType& operator= (const JackAudioIODeviceType&);
};

#ifndef USING_RELAYTOOL_FOR_LIBJACK
#define USING_RELAYTOOL_FOR_LIBJACK 1
#endif

#if USING_RELAYTOOL_FOR_LIBJACK
extern "C" int libjack_is_present;
#endif

namespace juce {
//==============================================================================
AudioIODeviceType* juce_createJackAudioIODeviceType()
{
#if USING_RELAYTOOL_FOR_LIBJACK
    /* detect if libjack.so is available using relaytool on linux */
    if (!libjack_is_present) return 0;
#endif
    /* detect if the jack framework is available on macos
       (requires linking with -weak_framework Jackmd) */
    if ((void*)&jack_client_open == 0)
        return 0;
    return new JackAudioIODeviceType();
}
} // namespace juce


and the jack_device.hh file contains:
Code: Select all
struct JackClientConfiguration {
  String      clientName;
  // size of array = number of input channels. If the strings are empty, default names are chosen (in_1 , in_2 etc)
  StringArray inputChannels;
  StringArray outputChannels;
};

// user supplied function for jack config
void getJackClientConfiguration(JackClientConfiguration &conf);


That way my application supplies the number of ports it wants , and does not deal at all with connections to other jack ports . I have tested it on macos also and it works (but it is not very useful since jackosx also create a fake coreaudio device)

The relaytool ( http://autopackage.org/apbuild-relaytool.php ) stuff is to build a binary that is not "hardlinked" to libjack.so, the result is equivalent to weak linking on macos
jpo
JUCE UberWeenie
 
Posts: 336
Joined: Thu Mar 20, 2008 2:45 pm

PreviousNext

Return to Linux

Who is online

Users browsing this forum: No registered users and 1 guest