Jack crash

classic Classic list List threaded Threaded
2 messages Options
Reply | Threaded
Open this post in threaded view
|

Jack crash

"Jørn V. Christensen"
Hi alll good people.

I am writing a program to trigger some sound effects for a theater. The
idea is to you fast and easy can start and kill background music on one
set of channels and sound effects on another and so on...

I am using Jack (obiously) and all is fine - except for some strange
crashes. As far as I can see it crashes two places: When I request port
information (jack_get_ports) and when I unregister my ports
(jack_port_unregister).

The crash in jack_get_ports is not reproducible for me and I cannot give
you further info. It happens perhaps one in a 100.

The crash in jack_port_unregister is reproducible for me. It happens if
I register 4 times 2 ports (stereo files) - e.g. A B C D (where each
letter reprsents an audio file with 2 ports registered) - and then kill
them in the order of B C D A. It then crashed when trying to unregister
the ports of audio file A.

I hoped one of you could tell what I have done wrong; I attached the
file containing my code communicating with jack - if I'm lucky, you have
nothing better to do than looking through my code :)

A note on my system:
jvc@jvc:~$ uname -a
Linux jvc 2.6.12.2 #1 Wed Jul 6 14:35:57 CEST 2005 i686 GNU/Linux
jvc@jvc:~$ jackd --version
jackd version 0.99.0 tmpdir /dev/shm protocol 13

2.4GHz, 512 MB RAM

Best regards
Jørn Christensen



-------------------------------------------------------
SF.Net email is Sponsored by the Better Software Conference & EXPO
September 19-22, 2005 * San Francisco, CA * Development Lifecycle Practices
Agile & Plan-Driven Development * Managing Projects & Teams * Testing & QA
Security * Process Improvement & Measurement * http://www.sqe.com/bsce5sf
_______________________________________________
Jackit-devel mailing list
[hidden email]
https://lists.sourceforge.net/lists/listinfo/jackit-devel
Reply | Threaded
Open this post in threaded view
|

Re: Jack crash

"Jørn V. Christensen"
Jørn V. Christensen wrote:

> Hi alll good people.
>
> I am writing a program to trigger some sound effects for a theater.
> The idea is to you fast and easy can start and kill background music
> on one set of channels and sound effects on another and so on...
>
> I am using Jack (obiously) and all is fine - except for some strange
> crashes. As far as I can see it crashes two places: When I request
> port information (jack_get_ports) and when I unregister my ports
> (jack_port_unregister).
>
> The crash in jack_get_ports is not reproducible for me and I cannot
> give you further info. It happens perhaps one in a 100.
>
> The crash in jack_port_unregister is reproducible for me. It happens
> if I register 4 times 2 ports (stereo files) - e.g. A B C D (where
> each letter reprsents an audio file with 2 ports registered) - and
> then kill them in the order of B C D A. It then crashed when trying to
> unregister the ports of audio file A.
>
> I hoped one of you could tell what I have done wrong; I attached the
> file containing my code communicating with jack - if I'm lucky, you
> have nothing better to do than looking through my code :)
>
> A note on my system:
> jvc@jvc:~$ uname -a
> Linux jvc 2.6.12.2 #1 Wed Jul 6 14:35:57 CEST 2005 i686 GNU/Linux
> jvc@jvc:~$ jackd --version
> jackd version 0.99.0 tmpdir /dev/shm protocol 13
>
> 2.4GHz, 512 MB RAM
>
> Best regards
> Jørn Christensen
BTW - I forgot (dont know if it has any relevance) - my program (except
for the file attached) is written in C++, using QT3 for GUI.

And then I forgot to attach the file... Now it should be right :)

~Jørn

#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <unistd.h>
#include <string.h>

#include "jackctrl.h"

/********* int init_jackctrl(global_info* gi) **********
*  This function should
*  - create a jack-connection
*  - create the process thread (really done by jack)
*  - create disk thread process
*  - initialize read/write lock  (linked audio list; obtain write lock before deleting entry)
*  - initialize condition/mutex  (signal jproc => disk_read - requesting data)
*  - return 0 on success
*******************************************************/
int init_jackctrl(global_info *gi) {
        if(gi == NULL) {
                fprintf (stderr, "Missing argument for init_jackctrl!\n");
                return -1;
        }

        gi->client = 0;
        gi->internalInterlacedBuffer = 0;
        gi->internalBufferArray = 0;
        gi->jack_samplerate = 0;
        gi->audioList = 0;
        gi->audioIdCounter = 0;
        gi->audioSampleSize = sizeof(jack_default_audio_sample_t);

        gi->thread_status = 0;

        if(gi->clientName == NULL) {
                fprintf (stderr, "No client name specified!\n");
                return -1;
        }


        if((gi->client = jack_client_new(gi->clientName)) == NULL) {
                fprintf (stderr, "Could not connect to jack.\nThis can be caused by several things:\n");
                fprintf (stderr, "Jack server not running?\n");
                fprintf (stderr, "Jack server running as another user?\n");
                fprintf (stderr, "Client with name %s already registered?\n", gi->clientName);
                return -1;
        }

        // This func does not return any status like the other funcs. Dont why...?
        jack_on_shutdown(gi->client, jack_shutdown, gi);

        if(jack_set_process_callback(gi->client, jprocess, gi)) {
                fprintf (stderr, "Could not set process callback function.\n");
                fprintf (stderr, "Disconnecting...\n");
                jack_client_close(gi->client);
                return -1;
        }

        if(gi->debugLevel > 0)
                if(jack_is_realtime(gi->client) == 0) {
                        fprintf(stderr, "WARNING: Jack server not running realtime mode.\n");
                        fprintf(stderr, "         You would probably be getting a better performance by\n");
                        fprintf(stderr, "         starting jackd with realtime mode (-R option).\n");
                }

        if(jack_set_sample_rate_callback(gi->client, set_samplerate, gi)) {
                fprintf (stderr, "Could not set sample rate callback function\n");
                fprintf (stderr, "Disconnecting...\n");
                jack_client_close(gi->client);
                return -1;
        }
       
        if((gi->internalInterlacedBuffer = (char*) malloc(gi->internalBufferSize * gi->maxAudioChannels)) == NULL) {
                fprintf (stderr, "Could not initialize internal buffers (interlaced).\n");
                fprintf (stderr, "Make sure you have enough system ressources available.\n");
                fprintf (stderr, "Disconnecting...\n");
                jack_client_close(gi->client);
                return -1;
        }

        if((gi->internalBufferArray = (char*) malloc(gi->internalBufferSize * gi->maxAudioChannels)) == NULL) {
                fprintf (stderr, "Could not initialize internal buffers (array).\n");
                fprintf (stderr, "Make sure you have enough system ressources available.\n");
                fprintf (stderr, "Disconnecting...\n");
                jack_client_close(gi->client);
                return -1;
        }

        gi->jackBufSize = jack_get_buffer_size(gi->client);
        if(jack_set_buffer_size_callback(gi->client, set_jackbufsize, gi)) {
                fprintf (stderr, "Could not set buffer_size_change callback function\n");
                fprintf (stderr, "Disconnecting...\n");
                jack_client_close(gi->client);
                return -1;
        }

        if(pthread_cond_init(&(gi->signal_condition), NULL)) {
                fprintf (stderr, "Could not initialize internal signal condition (pthreads).\n");
                fprintf (stderr, "Make sure you have enough system ressources available.\n");
                fprintf (stderr, "Disconnecting...\n");
                jack_client_close(gi->client);
                return -1;
        }
        gi->thread_status |= THREAD_STATUS_SIG_COND;

        pthread_mutex_init(&(gi->condition_mutex), NULL);   // Always returns 0
        gi->thread_status |= THREAD_STATUS_COND_MUT;

        pthread_rwlock_init(&(gi->rwlock), NULL);
        gi->thread_status |= THREAD_STATUS_RWLOCK;

        if(pthread_create(&(gi->pthread), NULL, disk_read, gi)) {
                fprintf(stderr, "Could not launch new thread.\n");
                fprintf(stderr, "Make sure you have enough system ressources available.\n");
                fprintf (stderr, "Disconnecting...\n");
                jack_client_close(gi->client);
                return -1;
        }
        gi->thread_status |= THREAD_STATUS_PTHREAD;


        if(gi->debugLevel > 1) {
                const char **ports;
                int i = 0;
                fprintf(stderr, "INFO: Output ports:\n");

                if((ports = jack_get_ports(gi->client, NULL, NULL, JackPortIsInput)) == NULL) {
                        if(gi->debugLevel > 0)
                                fprintf(stderr, "WARNING: Could not get jack ports!\n");
                }
                else {
                        while(*(ports+i)) {
                                fprintf(stderr, "INFO: Port %d: %s\n", i, *(ports+i));
                                i++;
                        }

                        free(ports);
                }
        }

        gi->audioSampleSize = sizeof(jack_default_audio_sample_t);

        if (jack_activate (gi->client)) {
                fprintf (stderr, "Could not activate client.\n");
                fprintf (stderr, "Disconnecting...\n");
                jack_client_close(gi->client);
                return -1;
        }
       
        return 0;
}

/******** void stop_jackctrl(global_info *gi) **********
*  This function cleans up after init_jackctrl(...)
*******************************************************/
void stop_jackctrl(global_info *gi) {
        if(gi->debugLevel > 1)
                fprintf(stderr, "INFO: Cleaning up... ");

        if(gi->thread_status & THREAD_STATUS_PTHREAD) {
                pthread_cancel(gi->pthread);
                pthread_join(gi->pthread, NULL);
        }

        if(gi->client) {
                jack_deactivate(gi->client);
                jack_client_close(gi->client);
                gi->client = 0;
        }

        audio_entry *delEntry, *entry = gi->audioList;
        while(entry) {
                delEntry = entry;
                entry = entry->next;
                destroy_audio_entry(delEntry);
        }
        gi->audioList = 0;

        if(gi->thread_status & THREAD_STATUS_RWLOCK)
                pthread_rwlock_destroy(&(gi->rwlock));
        if(gi->thread_status & THREAD_STATUS_COND_MUT)
                pthread_mutex_destroy(&(gi->condition_mutex));
        if(gi->thread_status & THREAD_STATUS_SIG_COND)
                pthread_cond_destroy(&(gi->signal_condition));

        gi->thread_status = 0;

        if(gi->internalInterlacedBuffer)
                free(gi->internalInterlacedBuffer);
        gi->internalInterlacedBuffer = 0;

        if(gi->internalBufferArray)
                free(gi->internalBufferArray);
        gi->internalBufferArray = 0;
       
        if(gi->debugLevel > 1)
                fprintf(stderr, "Done.\n");
}

/*********** void jack_shutdown(void *arg) ************
*  Called when jack shuts down.
*  What to do..?? What to dooo????
*******************************************************/
void jack_shutdown(void *arg) {
        global_info *gi = (global_info *) arg;

        if(gi->debugLevel > 0)
                fprintf(stderr, "WARNING: Jack shutting down or we have been to slow and\n"
                                                "         therefore disconnected.\n");
        gi->client = 0;
        stop_jackctrl(gi);
}


/*** int jprocess(jack_nframes_t nframes, void *arg) ***
*  The jack callback function.
*  Shovel some data...
*******************************************************/
int jprocess(jack_nframes_t nframes, void *arg) {
        jack_port_t **jack_port; // Pointer for iterating
        jack_default_audio_sample_t *jack_portBuffer;
        jack_ringbuffer_t **ringbuffer; // Pointer for iterating
        uint doWriteBytes = 0; // bytes = audioSampleSize * frames (for each channel/buffer)
        uint chnCnt, chnEOB, reqBytes, bytesWritten;
        uint minBytesWritten;
        jack_default_audio_sample_t damp, stepSize;
        uint j, framesWritten;
        jack_default_audio_sample_t *portPtr, *vuPtr;

        global_info *gi = (global_info *) arg;
        // Get read-lock (so no items in the audio-list will be deleted!)
        pthread_rwlock_rdlock(&(gi->rwlock));

        audio_entry *entry = gi->audioList;
        while(entry) {
                // Port and buffer pointers
                jack_port = entry->jack_ports;
                ringbuffer = entry->ringbuffers;
                chnEOB = 0;
                minBytesWritten = -1;

                reqBytes = nframes * gi->audioSampleSize;
                for(chnCnt = 0; chnCnt < entry->channels; chnCnt++) {
                        if((jack_portBuffer = (jack_default_audio_sample_t *) jack_port_get_buffer(*jack_port, nframes)) == NULL) {
                                fprintf(stderr, "ERROR: Failed getting a portbuffer for audio entry %s, port %s.\n",
                                        entry->name, jack_port_name(*jack_port));
                                continue;
                        }

                        bytesWritten = 0;

                        if(entry->fillZero < 2 && entry->eob == 0) {
                                doWriteBytes = jack_ringbuffer_read_space(*ringbuffer);
                                if(doWriteBytes) {
                                        doWriteBytes -= doWriteBytes % gi->audioSampleSize;
                                        if(reqBytes < doWriteBytes)
                                                doWriteBytes = reqBytes;
                                        else
                                                if(entry->eof == 0)
                                                        fprintf(stderr, "WARNING: Ringbuffer underrun!\n");

                                        if(doWriteBytes) {
                                                bytesWritten = jack_ringbuffer_read(*ringbuffer, (char *) jack_portBuffer, doWriteBytes);
                                                if(bytesWritten != doWriteBytes)
                                                        fprintf(stderr, "ERROR: Did not write specified bytes to jack!\n");
                                        }
                                }
                                else {
                                        if(entry->eof)
                                                chnEOB++;
                                        else
                                                fprintf(stderr, "WARNING: Ringbuffer empty and file was not EOF!\n");
                                }
                        }

                        // Fill jack port buffer with zero for missing data.
                        if(bytesWritten != reqBytes)
                                bzero((void *) (jack_portBuffer + bytesWritten), reqBytes - bytesWritten);

                        // Kill the sound softly
                        if(entry->fillZero == 2) {
                                entry->eob = 1;
                                if(gi->debugLevel > 1)
                                        fprintf(stderr, "INFO: Marking %s for deletion.\n", entry->name);
                        }
                        else if(entry->fillZero == 1) {
                                damp = 1.0;
                                framesWritten = bytesWritten / gi->audioSampleSize;
                                stepSize = 1.0 / (float) framesWritten;
                                portPtr = jack_portBuffer;
                                for(j = 0; j < framesWritten; j++) {
                                        *portPtr *= damp;
                                        damp -= stepSize;
                                        portPtr++;
                                }
                                entry->fillZero = 2;
                        }


                        // Calculate VU
                        portPtr = jack_portBuffer;
                        vuPtr = entry->vu + chnCnt;
// *vuPtr = 0;  Do not clear - GUI will do that when reading it!
                        float absVal;
                        for(j = 0; j < nframes; j++) {
                                absVal = fabsf(*portPtr);
                                if(absVal > *vuPtr)
                                        *vuPtr = absVal;
                                portPtr++;
                                j++;
                        }

                        if(minBytesWritten > bytesWritten)
                                minBytesWritten = bytesWritten;
       
                        // Shift buffers (process next channel)
                        jack_port++;
                        ringbuffer++;
                }

                // End-of-buffers - all audio played
                if(chnEOB == entry->channels) {
                        entry->eob = 1;
                        if(gi->debugLevel > 1)
                                fprintf(stderr, "INFO: EOB of %s.\n", entry->name);
                }

                // Update position and shift audio entry
                entry->currentFrame += minBytesWritten / gi->audioSampleSize;
                entry = entry->next;
        }


        // Think we are done, so release delloc and
        // signal disk_thread that we are thirsty for more...
        pthread_rwlock_unlock(&(gi->rwlock));
        if(pthread_mutex_trylock(&(gi->condition_mutex)) == 0) {
                pthread_cond_signal(&(gi->signal_condition));
                pthread_mutex_unlock(&(gi->condition_mutex));
        }

        return 0;
}

/******** int set_samplerate(jack_nframes_t nframes, void *arg) **********
*  This function is called whenever jack changes its samplerate.
*  It is also called once when connecting to jack.
**************************************************************************/
int set_samplerate(jack_nframes_t nframes, void *arg) {
        global_info *gi = (global_info *) arg;

        if(gi->jack_samplerate == 0) {
                if(gi->debugLevel > 1)
                        fprintf(stderr, "INFO: Jack samplerate: %d\n", nframes);
        }
        else {
                if(gi->debugLevel > 0)
                        fprintf(stderr, "WARNING: Jack is changing samplerate.\n");
        }

        gi->jack_samplerate = nframes;

        return 0;
}

/******** int set_jackbufsize(jack_nframes_t nframes, void *arg) **********
*  This function is called whenever jack changes its port buffer size.
**************************************************************************/
int set_jackbufsize(jack_nframes_t nframes, void *arg) {
        global_info *gi = (global_info *) arg;

        if(gi->debugLevel > 0)
                fprintf(stderr, "WARNING: Jack is changing its buffer size to %d.\n", nframes);

        gi->jackBufSize = nframes;

        return 0;
}


/********** void *disk_read(void *arg) ************
*  PThread that takes care of shoveling data from
*  disk to ringbuffers.
***************************************************/
void *disk_read(void *arg) {
        audio_entry *entry, *lastEntry = NULL, *delEntry;
        int i;

        global_info *gi = (global_info *) arg;

        // Lock signal/condition mutex
        pthread_mutex_lock(&(gi->condition_mutex));

        while(1) {
                i = pthread_cond_wait(&(gi->signal_condition), &(gi->condition_mutex));

                // Loop though list and delete eob entrys
                entry = gi->audioList;
                lastEntry = NULL;

                while(entry) {
                        if(entry->eob) { // eob => take out of list and delete
                                if(gi->debugLevel > 1)
                                        fprintf(stderr, "INFO: EOF and EOB of %s => removing.\n", entry->name);

                                if(lastEntry == NULL) {
                                        pthread_rwlock_wrlock(&(gi->rwlock));
                                        gi->audioList = entry->next;
                                        unregisterJackBuffers(entry);
                                        pthread_rwlock_unlock(&(gi->rwlock));
                                }
                                else {
                                        pthread_rwlock_wrlock(&(gi->rwlock));
                                        lastEntry->next = entry->next;
                                        unregisterJackBuffers(entry);
                                        pthread_rwlock_unlock(&(gi->rwlock));
                                }

                                sleep(0);  // Give up CPU so that other processes (jprocess) may get some control.

                                delEntry = entry;
                                entry = entry->next;
                                destroy_audio_entry(delEntry);
                        }
                        else {
                                lastEntry = entry;
                                entry = entry->next;
                        }
                }

                // Now - get some data....
                entry = gi->audioList;
                while(entry) {
                        fillEntryBuffers(entry);
                        entry = entry->next;
                }
        }

        return 0;
}

/**** void fillEntryBuffers(audio_entry *entry) ****
*  A helpter thread to disk_thread.
*  It fill the buffers of a audio_entry.
*  Is put into this function, so the buffers can be
*  filled from start from open_audio_entry(...).
***************************************************/
void fillEntryBuffers(audio_entry *entry) {
        jack_ringbuffer_t **ringbuffer;
        jack_default_audio_sample_t *ptr, *ptr_eof;
        uint availSpace, framesRead, doCopyFrames;
        uint i, j;
        global_info *gi = entry->gi;

        // Check that we have not prev marked this as eof
        if(entry->eof)
                return;
                       
        // Find minimum space in buffers
        ringbuffer = entry->ringbuffers;
        availSpace = jack_ringbuffer_write_space(*ringbuffer);
        ringbuffer++;
        for(i = 1; i < entry->channels; i++) {
                if((j = jack_ringbuffer_write_space(*ringbuffer)) < availSpace)
                        availSpace = j;
                ringbuffer++;
        }

        while(availSpace > gi->audioSampleSize && entry->eof == 0) {
                if(availSpace < gi->internalBufferSize)
                        doCopyFrames = availSpace / gi->audioSampleSize;
                else
                        doCopyFrames = gi->internalBufferSize / gi->audioSampleSize;

                framesRead = sf_readf_float(entry->sf_sndfile, (float *) gi->internalInterlacedBuffer, doCopyFrames);
                if(framesRead != doCopyFrames) {
                        entry->eof = 1;
                        if(gi->debugLevel > 1)
                                fprintf(stderr, "INFO: EOF of %s.\n", entry->name);
                }
                               
                ptr = (jack_default_audio_sample_t *) gi->internalInterlacedBuffer;
                ptr_eof = (jack_default_audio_sample_t *) (ptr + framesRead * entry->channels);
                j = 0;

                while(ptr < ptr_eof) {
                        for(i = 0; i < entry->channels; i++) {
                                *((jack_default_audio_sample_t *) (gi->internalBufferArray + i * gi->internalBufferSize + j * gi->audioSampleSize)) = *ptr * entry->volume;
                                ptr++;
                        }
                        j++;
                }
                               
                if(j != framesRead)
                        fprintf(stderr, "ERROR: Frames intented to read and frames actually read into ringbuffer differs!\n"
                                        "       Intended to read: %u. Actually read: %u.\n", framesRead, j);

                ringbuffer = entry->ringbuffers;
                j *= gi->audioSampleSize;
                for(i = 0; i < entry->channels; i++) {
                        jack_ringbuffer_write(*ringbuffer, gi->internalBufferArray + i * gi->internalBufferSize, j);
                        ringbuffer++;
                }

                availSpace -= j;
        }
}

/*** void destroy_audio_entry(audio_entry *entry) ****
*  As the name indicates...
******************************************************/
void destroy_audio_entry(audio_entry *entry) {
        jack_ringbuffer_t **ringbuffer;
        uint i = 0;

        if(entry->filename)
                free(entry->filename);

        if(entry->name)
                free(entry->name);

        if(entry->sf_sndfile) {
                sf_close(entry->sf_sndfile);
                free(entry->sf_sndfile);
        }

        if(entry->sf_info)
                free(entry->sf_info);

        if(entry->ringbuffers) {
                ringbuffer = entry->ringbuffers;
                for(i = 0 ; i < entry->channels; i++) {
                        if(*ringbuffer)
                                jack_ringbuffer_free(*ringbuffer);
                        ringbuffer++;
                }
                free(entry->ringbuffers);
        }

// unregisterJackBuffers(entry);

        free(entry);
}

/*** void unregisterJackBuffers(audio_entry *entry) ****
*  Unregister jack ports so they wont produce noise
*  when they are not filled in the next jprocess call.
*******************************************************/
void unregisterJackBuffers(audio_entry *entry) {
        jack_port_t **jack_port;
        uint i;
fprintf(stderr, "unreg jack buf... ");

        if(entry->gi->client) {
                if(entry->jack_ports) {
                        jack_port = entry->jack_ports;
                        for(i = 0 ; i < entry->channels; i++) {
                                if(*jack_port) {
fprintf(stderr, " %s ", jack_port_name(*jack_port));
                                        jack_port_unregister(entry->gi->client, *jack_port);
                                        free(*jack_port);
                                        *jack_port = 0;
                                }
                                jack_port++;
                        }
                        free(entry->jack_ports);
                        entry->jack_ports = 0;
                }
        }
fprintf(stderr, " done\n");
}


/*********** void take_out_audio_entry(...) ************
*  Take out entry from the linked list and destroy it.
*  NB: Not any more - just mark it for deletion!
*******************************************************/
void take_out_audio_entry(audio_entry *delEntry) {
        delEntry->fillZero = 1;
}

/****** int open_audio_entry(...) ******
*  As the name indicates...
*  Should also allocate ringbuffers, jackports
*  and insert it to the chain.
*  BTW - it should also connect its output port to
*  a jack input port.
*******************************************************/
audio_entry * open_audio_entry(global_info *gi, const char *filename, const char *name, port_connection *connections, float volume) {
        uint i, j;
        char *buf;
        char *namePtr;
        audio_entry *entry;

        SF_INFO *sfinfo;
        SNDFILE *sndfile;
        jack_port_t** portPtr;
        jack_ringbuffer_t **ringbufferPtr;
        uint portNameLength, nameTruncated;

        port_connection *oldConn;
        char *srcPort;
        uint ringbufSize;
        port_info *portInfo;

        entry = (audio_entry *) malloc(sizeof(audio_entry));
        if(entry == NULL)
                return NULL;

        entry->gi = gi;
        entry->filename = NULL;
        entry->name = NULL;
        entry->sf_info = NULL;
        entry->sf_sndfile = NULL;
        entry->ringbuffers = NULL;
        entry->jack_ports = NULL;
        entry->next = 0;
        entry->currentFrame = 0;
        entry->error = 0;
        entry->eof = 0;
        entry->eob = 0;
        entry->fillZero = 0;
        entry->volume = volume;
        entry->vu = 0;

        if(gi->client == NULL)
                entry->error |= ERR_NO_JACK_CLIENT;

        buf = (char *) malloc(portNameLength = jack_port_name_size());
        if(buf == NULL)
                entry->error |= ERR_COULD_NOT_ALLOCATE_MEM;
       
        if(portNameLength > (i = strlen(gi->clientName) + 2))
                portNameLength -= i;
        else
                entry->error |= ERR_TO_LONG_CLIENT_NAME;

        if(filename == NULL)
                entry->error |= ERR_MISSING_FILENAME;

        if(entry->error)
                return entry;

        entry->filename = (char *) malloc(strlen(filename) + 1);
        if(entry->filename == NULL) {
                entry->error |= ERR_COULD_NOT_ALLOCATE_MEM;
                return entry;
        }
        strcpy(entry->filename, filename);

        if(access(entry->filename, F_OK)) {
                entry->error = ERR_FILE_NOT_FOUND;
                return entry;
        }

        if(name) {
                entry->name = (char *) malloc(strlen(name) + 1);
                if(entry->name == NULL) {
                        entry->error |= ERR_COULD_NOT_ALLOCATE_MEM;
                        return entry;
                }
                strcpy(entry->name, name);
        }

        sfinfo = (SF_INFO*) malloc(sizeof(SF_INFO));
        if(sfinfo == NULL) {
                entry->error |= ERR_COULD_NOT_ALLOCATE_MEM;
                return entry;
        }

        sndfile = sf_open(entry->filename, SFM_READ, sfinfo);
        if(sndfile) {
                if((uint) sfinfo->channels > gi->maxAudioChannels) {
                        entry->error = ERR_TO_MANY_CHANNELS;
                        free(sfinfo);
                        free(sndfile);
                        return entry;
                }

                entry->vu = (jack_default_audio_sample_t *) malloc(gi->audioSampleSize * sfinfo->channels);
                if(entry->vu == NULL)
                        entry->error |= ERR_COULD_NOT_ALLOCATE_MEM;

                for(i = 0; i < (uint) sfinfo->channels; i++)
                        *(entry->vu + i) = 0.0;

                if(sf_command(sndfile, SFC_SET_NORM_FLOAT, NULL, SF_TRUE) != 1) {
                        entry->error = ERR_SETTING_NORMALIZE;
                        free(sfinfo);
                        free(sndfile);
                        return entry;
                }


                entry->totalFrames = sfinfo->frames;
                entry->samplerate = sfinfo->samplerate;
                entry->channels = sfinfo->channels;
                entry->sf_info = sfinfo;
                entry->sf_sndfile = sndfile;
                entry->id = gi->audioIdCounter++;


                // --------------------------------
                // Register ports
                // --------------

                if(entry->name)
                        namePtr = entry->name;
                else
                        namePtr = entry->filename;

                // Allocate space for ports and point to NULL
                entry->jack_ports = malloc(sizeof(jack_port_t *) * entry->channels);
                if(entry->jack_ports == NULL) {
                        entry->error |= ERR_COULD_NOT_ALLOCATE_MEM;
                        return entry;
                }

                portPtr = entry->jack_ports;
                for(i = 0; i < entry->channels; i++) {
                        *portPtr = NULL;
                        portPtr++;
                }

                // Register ports with jack
                portPtr = entry->jack_ports;
                for(i = 0; i < entry->channels; i++) {
                        nameTruncated = 0;
                        j = snprintf(buf, portNameLength, "%s[%d]_%d", namePtr, entry->id, i);
                        if(j > portNameLength) {
                                nameTruncated = 1;
                                if(gi->debugLevel > 0)
                                        fprintf(stderr, "WARNING: Port name truncated to\n         %s.\n", buf);
                        }

                        *portPtr = jack_port_register(gi->client, buf, JACK_DEFAULT_AUDIO_TYPE, JackPortIsOutput|JackPortIsTerminal, 0);
                        if(*portPtr == NULL && nameTruncated) {
                                if(gi->debugLevel > 0)
                                        fprintf(stderr, "WARNING: Could not register port. Name was truncated, so trying a shorter name...\n");
                                j = snprintf(buf, portNameLength, "[%d]_%d", entry->id, i);
                                if(j > portNameLength) {
                                        if(gi->debugLevel > 0)
                                                fprintf(stderr, "WARNING: Port name truncated to\n         %s.\n", buf);
                                }

                                *portPtr = jack_port_register(gi->client, buf, JACK_DEFAULT_AUDIO_TYPE, JackPortIsOutput|JackPortIsTerminal, 0);
                        }

                        if(*portPtr == NULL) {
                                entry->error |= ERR_REG_JACK_PORT;
                                return entry;
                        }

                        if(gi->debugLevel > 1)
                                fprintf(stderr, "INFO: Registered port %s.\n", buf);

                        portPtr++;
                }



                // --------------------------------
                // Connect ports
                // -------------
                portInfo = get_jack_ports_info(gi);
                if(portInfo == NULL) {
                        entry->error |= ERR_GET_PORT_INFO;
                        return entry;
                }

               
                while(connections) {
                        if(connections->srcChannel < entry->channels) {
                                srcPort = (char *) jack_port_name(*(entry->jack_ports + connections->srcChannel));
                                if(connections->dstName) {
                                        if(gi->debugLevel > 1) {
                                                fprintf(stderr, "INFO: Connecting ports %s to %s... ", srcPort, connections->dstName);
                                                if(jack_connect(gi->client, srcPort, connections->dstName) == 0)
                                                        fprintf(stderr, "OK.\n");
                                                else
                                                        fprintf(stderr, "FAILED!\n");
                                        }
                                        else
                                                if(jack_connect(gi->client, srcPort, connections->dstName))
                                                        if(gi->debugLevel > 0)
                                                                fprintf(stderr, "ERROR: Could not connect port %s to %s!\n", srcPort, connections->dstName);
                                }
                                else if(gi->debugLevel > 0)
                                        fprintf(stderr, "ERROR: No destination port given.\n");
                        }
                        else {
                                if(gi->debugLevel > 0)
                                        fprintf(stderr, "ERROR: Invalid audio channel number: %d.\n", connections->srcChannel);
                        }

                        oldConn = connections;
                        connections = connections->next;
                        free(oldConn);
                }

                free_port_info(portInfo);


                // --------------------------------
                // Create ringbuffers
                // ------------------

                // Allocate space for ringbuffers and point to NULL
                entry->ringbuffers = malloc(sizeof(jack_ringbuffer_t *) * entry->channels);
                if(entry->ringbuffers == NULL) {
                        entry->error |= ERR_COULD_NOT_ALLOCATE_MEM;
                        return entry;
                }

                ringbufferPtr = entry->ringbuffers;
                for(i = 0; i < entry->channels; i++) {
                        *ringbufferPtr = NULL;
                        ringbufferPtr++;
                }

                ringbufferPtr = entry->ringbuffers;
                ringbufSize = gi->ringbufferSizeFactor * gi->jackBufSize * gi->audioSampleSize;
                for(i = 0; i < entry->channels; i++) {
                        if(gi->debugLevel > 1)
                                fprintf(stderr, "INFO: Requesting ringbuffer (size: %u)... ", ringbufSize);
                       
                        if((*ringbufferPtr = jack_ringbuffer_create(ringbufSize))) {
                                if(gi->debugLevel > 1)
                                        fprintf(stderr, "OK\n");
                        }
                        else {
                                if(gi->debugLevel > 1)
                                        fprintf(stderr, "FAILED!\n");

                                entry->error |= ERR_COULD_NOT_RINGBUF;
                                return entry;
                        }
                        ringbufferPtr++;
                }

               
                // ----------------------------------
                // Fill buffers and insert into chain
                // ------------------
                fillEntryBuffers(entry);
                pthread_rwlock_rdlock(&(gi->rwlock));
                entry->next = gi->audioList;
                gi->audioList = entry;
                pthread_rwlock_unlock(&(gi->rwlock));
        }
        else {
                entry->error = ERR_FILE_COULD_NOT_SFOPEN;
                free(sfinfo);
        }

        return entry;
}

/*********** file_info *get_file_info(...) ***********
*  Get fileinfo.
*  The pointer *filename will be used in
*  file_info->filename.
*****************************************************/
file_info *get_file_info(global_info *gi, const char *filename) {
        file_info *a;
        SF_INFO *sfinfo;
        SNDFILE *sndfile;

        a = (file_info*) malloc(sizeof(file_info));
        a->error = 0;

        if(filename == NULL) {
                a->error |= ERR_MISSING_FILENAME;
                return a;
        }

        a->filename = filename;

        if(access(filename, F_OK)) {
                a->error = ERR_FILE_NOT_FOUND;
                return a;
        }

        sfinfo = (SF_INFO*) malloc(sizeof(SF_INFO));
        sndfile = sf_open(filename, SFM_READ, sfinfo);

        if(sndfile) {
                a->totalFrames = sfinfo->frames;
                a->samplerate = sfinfo->samplerate;
                a->channels = sfinfo->channels;
                if(a->channels > gi->maxAudioChannels)
                        a->error = ERR_TO_MANY_CHANNELS;

                free(sfinfo);
                free(sndfile);
        }
        else {
                a->error = ERR_FILE_COULD_NOT_SFOPEN;
                free(sfinfo);
        }

        return a;
}

/*** port_info *get_jack_ports_info(global_info *gi) ***
* Basically just a wrapper...
********************************************************/
port_info *get_jack_ports_info(global_info *gi) {
        const char **ports;
        int i = 0;

        if(gi->client == NULL)
                return NULL;

        port_info *a = (port_info *) malloc(sizeof(port_info));
        if(a == NULL)
                return NULL;

        if((ports = jack_get_ports(gi->client, NULL, NULL, JackPortIsInput)) == NULL) {
                free(a);
                return NULL;
        }

        a->names = ports;

        while(*(ports+i))
                i++;

        a->count = i;

        return a;
}

/********* void free_port_info(port_info *p) ***********
* Freeing port_info.
* It seems stupid to have a function with only two
* statements in - but I almost forgot :(
********************************************************/
void free_port_info(port_info *p) {
        if(p) {
                if(p->names)
                        free(p->names);
                free(p);
        }
}

/***** void get_rdlock() - void release_rdlock() *******
* Just convenience functions to obtain and release
* read lock.
********************************************************/
void get_rdlock(global_info *gi) {
        pthread_rwlock_rdlock(&(gi->rwlock));
}
void release_rdlock(global_info *gi) {
        pthread_rwlock_unlock(&(gi->rwlock));
}

#ifndef COMMON
#define COMMON

#include <sndfile.h>
#include <pthread.h>
#include <jack/jack.h>
#include <jack/ringbuffer.h>

#ifdef __cplusplus
extern "C" {
#endif


typedef struct {
        int count;
    const char **names;
} port_info;

struct _port_connection;

typedef struct _port_connection {
        uint srcChannel;
        const char *dstName;
        struct _port_connection *next;
} port_connection;


typedef struct {
        const char *filename;
        int error;
        uint totalFrames;
        int samplerate;
    uint channels;
} file_info;

struct _audio_entry;
struct _global_info;

typedef struct _audio_entry {
        uint id;
        struct _global_info *gi;
        char *filename;
        char *name;
        int error;
        SF_INFO *sf_info;
        SNDFILE *sf_sndfile;
        jack_ringbuffer_t **ringbuffers;
        jack_port_t **jack_ports;
        int samplerate;
        uint channels;
        uint totalFrames;
        uint currentFrame;
        jack_default_audio_sample_t volume;
        jack_default_audio_sample_t *vu;
        uint eof; // end-of-file, set by disk_read
        uint eob; // end-of-buffers, set by jprocess => can be removed from list
        uint fillZero; // Fill jack buffer with zeros and mark eob next jprocess
        struct _audio_entry *next;
        void *gui_ptr; // Reserved to GUI. Will point to widget corresponding to the audio entry
} audio_entry;


#define THREAD_STATUS_RWLOCK 1 << 0
#define THREAD_STATUS_SIG_COND 1 << 1
#define THREAD_STATUS_COND_MUT 1 << 2
#define THREAD_STATUS_PTHREAD 1 << 3

typedef struct _global_info {
        // JACKCTRL-related
        jack_client_t *client;
    char *clientName;
        char *internalInterlacedBuffer;
        char *internalBufferArray;
        uint internalBufferSize;
        uint maxAudioChannels;
    uint ringbufferSizeFactor;
        uint jackBufSize;
        uint audioSampleSize;  // = sizeof(jack_default_audio_sample_t)
        uint jack_samplerate;
        audio_entry *audioList;
        uint audioIdCounter;
       
        // MISC
        int debugLevel;

    // Thread and mutexes
        uint thread_status;
        pthread_rwlock_t  rwlock;           // linked audio list; obtain write lock before deleting entry
        pthread_cond_t    signal_condition; // Signal jprocess => disk_read that data is requested
        pthread_mutex_t   condition_mutex;  // Mutex for above  -^-
        pthread_t pthread;
} global_info;



#ifdef __cplusplus
}
#endif

#endif

#ifndef JACKCTRL_H
#define JACKCTRL_H

#include "common.h"
#include "config.h"
#include "errmsg.h"

#ifdef __cplusplus
extern "C" {
#endif

int init_jackctrl(global_info* gi);
void stop_jackctrl(global_info *gi);
void jack_shutdown(void* arg);
int jprocess(jack_nframes_t nframes, void *arg);
int set_samplerate(jack_nframes_t nframes, void *arg);
int set_jackbufsize(jack_nframes_t nframes, void *arg);
void* disk_read(void* arg);
void fillEntryBuffers(audio_entry *entry);
void destroy_audio_entry(audio_entry* entry);
void unregisterJackBuffers(audio_entry *entry);
void take_out_audio_entry(audio_entry *delEntry);
audio_entry *open_audio_entry(global_info *gi, const char *filename, const char *name, port_connection *connections, float volume);
file_info *get_file_info(global_info* gi, const char* filename);
port_info *get_jack_ports_info(global_info *gi);
void free_port_info(port_info *p);
void get_rdlock(global_info *gi);
void release_rdlock(global_info *gi);

#ifdef __cplusplus
}
#endif

#endif