Ticket #2493: patch-macosx.diff

File patch-macosx.diff, 14.4 KB (added by danchr@…, 20 years ago)

Patch against mpg123 to go into files/

  • Makefile

    diff -Naur ../mpg123.orig/Makefile Makefile
    old new  
    5454        @echo "make netbsd         NetBSD"
    5555        @echo "make openbsd        OpenBSD"
    5656        @echo "make mint           MiNT on Atari"
     57        @echo "make macosx         Mac OS X"
     58        @echo "make macosx-dd      Mac OS X, with direct device access instead of AudioUnit"
    5759        @echo "make generic        try this one if your system isn't listed above"
    5860        @echo ""
    5961        @echo "Please read the file INSTALL for additional information."
     
    737739                -DREAL_IS_FLOAT -DMINT -DNOXFERMEM' \
    738740                AUDIO_LIB='-lsocket' \
    739741                mpg123-make
     742
     743macosx:
     744        $(MAKE) CC=cc LDFLAGS= \
     745                OBJECTS='decode.o dct64.o audio_macosx.o' \
     746                CFLAGS='$(CFLAGS) -DMAC_OS_X -Wall -O2' \
     747                LDFLAGS='-framework CoreAudio -framework AudioToolbox -framework AudioUnit -framework CoreServices' \
     748                mpg123-make
     749
     750macosx-dd:
     751        $(MAKE) CC=cc LDFLAGS= \
     752                OBJECTS='decode.o dct64.o audio_macosx.o' \
     753                CFLAGS='$(CFLAGS) -DMAC_OS_X -DMOSX_USES_AUDIOUNIT=0 -Wall -O2' \
     754                LDFLAGS='-framework CoreAudio' \
     755                mpg123-make
     756
    740757
    741758# maybe you need the additonal options LDFLAGS='-lnsl -lsocket' when linking (see solaris:)
    742759generic:
  • audio_macosx.c

    diff -Naur ../mpg123.orig/audio_macosx.c audio_macosx.c
    old new  
     1/*- This is a 80 chars line, to have pretty formatted comments ---------------*/
     2
     3/* audio_macosx.c, originally written by Guillaume Outters
     4 * to contact the author, please mail to: guillaume.outters@free.fr
     5 * AudioUnit version by Steven A. Kortze <skortze@sourceforge.net>, reviewed by
     6 * Guillaume Outters
     7 *
     8 * This file is some quick pre-alpha patch to allow me to have some music for my
     9 * long working days, and it does it well. But it surely isn't a final version;
     10 * as Mac OS X requires at least a G3, I'm not sure it will be useful to
     11 * implement downsampling.
     12 *
     13 * Mac OS X audio works by asking you to fill its buffer, and, to complicate a
     14 * bit, you must provide it with floats. In order not to patch too much mpg123,
     15 * we'll accept signed short (mpg123 "approved" format) and transform them into
     16 * floats as soon as received. Let's say this way calculations are faster.
     17 *
     18 * As we don't have some /dev/audio device with blocking write, we'll have to
     19 * stop mpg123 before it does too much work, while we are waiting our dump proc
     20 * to be called. We'll uses semaphores for that, after some time using an awful
     21 * sleep() / kill() solution while waiting for Apple to implement semaphores in
     22 * the Public Beta (well, they were implemented, but that ENOSYS looked somewhat
     23 * unfriendly to me) (or perhaps for me to learn how to use semaphores in the
     24 * Beta).
     25 *
     26 * In order always to have a ready buffer to be dumped when the routine gets
     27 * called, we have a "buffer loop" of NUMBER_BUFFERS buffers. mpg123 fills it
     28 * on one extremity ('to'), playProc reads it on another point ('from'). 'to'
     29 * blocks when it arrives on 'from' (having filled the whole circle of buffers)
     30 * and 'from' blocks when no data is available. As soon as it has emptied a
     31 * buffer, if mpg123 is sleeping, it awakes it quite brutaly (SIGUSR2) to tell
     32 * it to fill the buffer. */
     33
     34/* We can use either AudioUnits, or direct device addressing. */
     35
     36#ifndef MOSX_USES_AUDIOUNIT
     37#define MOSX_USES_AUDIOUNIT 1
     38#endif
     39
     40/* Mac OS X introduces AudioUnits v.2, sufficently different from version 1 to
     41 * require code changes. Unfortunately I don't know how to detect if version 2
     42 * is present, and I don't have a 10.1 machine to test it the empirical way. */
     43
     44#ifndef MOSX_AUDIOUNIT_V2
     45#define MOSX_AUDIOUNIT_V2 1
     46#endif
     47
     48#include "mpg123.h"
     49#if MOSX_USES_AUDIOUNIT
     50#include <CoreAudio/CoreAudio.h>
     51#include <AudioUnit/AudioUnit.h>
     52#include <AudioToolbox/DefaultAudioOutput.h>
     53#else
     54#include <CoreAudio/AudioHardware.h>
     55#endif
     56#include <stdio.h>
     57#include <stdlib.h>
     58#include <errno.h>
     59#include <semaphore.h>
     60
     61/* Didn't I tell you I had some paranoid behaviours? */
     62
     63#define G(troubleMaker,dest) if(err) { fprintf(stderr, "### %s: error %d\n", troubleMaker, (int)err); goto dest; }
     64#define DEB if(0) {
     65#define EB(x) x:
     66#define FEB }
     67
     68
     69struct aBuffer
     70{
     71        float * buffer;
     72        long size;
     73       
     74        float * ptr;    /* Where in the buffer are we? */
     75        long remaining;
     76       
     77        struct aBuffer * next;
     78};
     79typedef struct aBuffer aBuffer;
     80
     81struct anEnv
     82{
     83        long size;
     84        short * debut;
     85        short * ptr;
     86        #if MOSX_USES_AUDIOUNIT
     87        AudioUnit output;
     88        #else
     89        AudioDeviceID output;
     90        #endif
     91        char play;
     92       
     93        /* Intermediate buffers */
     94       
     95        sem_t * semaphore;
     96        aBuffer * from; /* Current buffers */
     97        aBuffer * to;
     98};
     99
     100static struct anEnv env;
     101
     102#define ENV ((struct anEnv *)inClientData)
     103#define NUMBER_BUFFERS 2        /* Tried with 3 buffers, but then any little window move is sufficient to stop the sound (OK, on a G3 400 with a Public Beta. Perhaps now we can go down to 2 buffers). With 16 buffers we have 1.5 seconds music buffered (or, if you're pessimistic, 1.5 seconds latency). Note 0 buffers don't work much further than the Bus error. */
     104
     105void destroyBuffers()
     106{
     107        aBuffer * ptr;
     108        aBuffer * ptr2;
     109       
     110        ptr = env.to->next;
     111        env.to->next = NULL;
     112        while(ptr)
     113        {
     114                ptr2 = ptr->next;
     115                if(ptr->buffer) free(ptr->buffer);
     116                free(ptr);
     117                ptr = ptr2;
     118        }
     119}
     120
     121int initBuffers()
     122{
     123        long m;
     124        aBuffer ** ptrptr;
     125       
     126        ptrptr = &env.to;
     127        for(m = 0; m < NUMBER_BUFFERS; m++)
     128        {
     129                *ptrptr = malloc(sizeof(aBuffer));
     130                (*ptrptr)->size = 0;
     131                (*ptrptr)->remaining = 0;
     132                (*ptrptr)->buffer = NULL;
     133                ptrptr = &(*ptrptr)->next;
     134                sem_post(env.semaphore);        /* This buffer is ready for filling (of course, it is empty!) */
     135        }
     136        *ptrptr = env.from = env.to;
     137       
     138        return 0; /* TO DO: error handling is a bit precarious here */
     139}
     140
     141int fillBuffer(aBuffer * b, short * source, long size)
     142{
     143        float * dest;
     144       
     145        if(b->remaining)        /* Non empty buffer, must still be playing */
     146                return(-1);
     147        if(b->size != size)     /* Hey! What's that? Coudn't this buffer size be fixed once (well, perhaps we just didn't allocate it yet) */
     148        {
     149                if(b->buffer) free(b->buffer);
     150                b->buffer = malloc(size * sizeof(float));
     151                b->size = size;
     152        }
     153       
     154        dest = b->buffer;
     155        while(size--)
     156                /* *dest++ = ((*source++) + 32768) / 65536.0; */
     157                *dest++ = (*source++) / 32768.0;
     158       
     159        b->ptr = b->buffer;
     160        b->remaining = b->size; /* Do this at last; we shouldn't show the buffer is full before it is effectively */
     161       
     162        #ifdef DEBUG_MOSX
     163        printf("."); fflush(stdout);
     164        #endif
     165       
     166        return(0);
     167}
     168
     169#if MOSX_USES_AUDIOUNIT
     170#if MOSX_AUDIOUNIT_V2
     171OSStatus playProc(void * inClientData, AudioUnitRenderActionFlags * inActionFlags, const AudioTimeStamp * inTimeStamp, UInt32 inBusNumber, UInt32 inNumFrames, AudioBufferList * outOutputData)
     172#else
     173OSStatus playProc(void * inClientData, AudioUnitRenderActionFlags inActionFlags, const AudioTimeStamp * inTimeStamp, UInt32 inBusNumber, AudioBuffer * ioData)
     174#endif
     175#else
     176OSStatus playProc(AudioDeviceID inDevice, const AudioTimeStamp * inNow, const AudioBufferList * inInputData, const AudioTimeStamp * inInputTime, AudioBufferList * outOutputData, const AudioTimeStamp * inOutputTime, void * inClientData)
     177#endif
     178{
     179        #define BUFFER_LIST ( ( ! MOSX_USES_AUDIOUNIT ) || MOSX_AUDIOUNIT_V2 )
     180       
     181        long m, n;
     182        float * dest;
     183       
     184        #if BUFFER_LIST
     185        int o;
     186       
     187        for(o = 0; o < outOutputData->mNumberBuffers; ++o)
     188        #endif
     189        {
     190                /* What we have to fill */
     191               
     192                #if ! BUFFER_LIST
     193                m = ioData->mDataByteSize / sizeof(float);
     194                dest = ioData->mData;
     195                #else
     196                m = outOutputData->mBuffers[o].mDataByteSize / sizeof(float);
     197                dest = outOutputData->mBuffers[o].mData;
     198                #endif
     199               
     200                while(m > 0)
     201                {
     202                        if( (n = ENV->from->remaining) <= 0 )   /* No more bytes in the current read buffer! */
     203                        {
     204                                while( (n = ENV->from->remaining) <= 0)
     205                                        usleep(2000);   /* Let's wait a bit for the results... */
     206                        }
     207                       
     208                        /* We dump what we can */
     209                       
     210                        if(n > m) n = m;        /* In fact, just the necessary should be sufficient (I think) */
     211                       
     212                        memcpy(dest, ENV->from->ptr, n * sizeof(float));
     213                       
     214                        /* Let's remember all done work */
     215                       
     216                        m -= n;
     217                        ENV->from->ptr += n;
     218                        if( (ENV->from->remaining -= n) <= 0)   /* ... and tell mpg123 there's a buffer to fill */
     219                        {
     220                                sem_post(ENV->semaphore);
     221                                ENV->from = ENV->from->next;
     222                        }
     223                }
     224        }
     225       
     226        return (0);
     227}
     228
     229int audio_open(struct audio_info_struct *ai)
     230{
     231        #if MOSX_USES_AUDIOUNIT
     232        #if MOSX_AUDIOUNIT_V2
     233        ComponentDescription desc;
     234        Component       comp;
     235        AURenderCallbackStruct inputCallback = { playProc, &env };
     236        #else
     237        struct AudioUnitInputCallback inputCallback = { playProc, &env };
     238        #endif
     239        #else
     240        long size;
     241        #endif
     242        OSStatus err;
     243        AudioStreamBasicDescription format;
     244        char s[10];
     245        long m;
     246       
     247        /* Where did that default audio output go? */
     248       
     249        #if MOSX_USES_AUDIOUNIT
     250        #if MOSX_AUDIOUNIT_V2
     251        desc.componentType = kAudioUnitType_Output;
     252        desc.componentSubType = kAudioUnitSubType_DefaultOutput;
     253        desc.componentManufacturer = kAudioUnitManufacturer_Apple; /* NOTE: and if default output isn't Apple? */
     254        desc.componentFlags = 0;
     255        desc.componentFlagsMask = 0;
     256       
     257        if(!(comp = FindNextComponent(NULL, &desc))) { fprintf(stderr, "### No default audio output component\n"); goto e0; }
     258        err = OpenAComponent(comp, &env.output); G("OpenAComponent()", e0)
     259        #else
     260        err = OpenDefaultAudioOutput(&env.output); G("OpenDefaultAudioOutput()", e0)
     261        #endif /* MOSX_AUDIOUNIT_V2 */
     262        err = AudioUnitInitialize(env.output); G("AudioUnitInitialize()", e1);
     263        #else
     264        size = sizeof(env.output);
     265        err = AudioHardwareGetProperty(kAudioHardwarePropertyDefaultOutputDevice, &size, &env.output); G("AudioHardwareGetProperty(DefaultOutputDevice)", e0)
     266        #endif
     267       
     268        /* Let's init our environment */
     269       
     270        env.size = 0;
     271        env.debut = NULL;
     272        env.ptr = NULL;
     273        env.play = 0;
     274       
     275        /* Hmmm, let's choose PCM format */
     276       
     277        #if MOSX_USES_AUDIOUNIT
     278        /* We tell the Output Unit what format we're going to supply data to it.
     279         * This is necessary if you're providing data through an input callback
     280         * AND you want the DefaultOutputUnit to do any format conversions
     281         * necessary from your format to the device's format. */
     282       
     283        format.mSampleRate = 44100.0; /* The sample rate of the audio stream */
     284        format.mFormatID = kAudioFormatLinearPCM; /* The specific encoding type of audio stream*/
     285        format.mFormatFlags = kLinearPCMFormatFlagIsFloat
     286                                                                | kLinearPCMFormatFlagIsBigEndian
     287                                                                | kLinearPCMFormatFlagIsPacked;         /* flags specific to each format */
     288        format.mBytesPerFrame = (format.mFramesPerPacket = 1) * (format.mBytesPerPacket = (format.mChannelsPerFrame = 2) * sizeof(float)); /* We produce 2-channel audio. Now if we have a mega-super-hyper card for our audio, it is its problem to convert it to 8-, 16-, 32- or 1024-channel data. */
     289        format.mBitsPerChannel = sizeof(float) * 8; /* One of the most constant constants of the whole computer history. */
     290       
     291        err = AudioUnitSetProperty(env.output, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input, 0, &format, sizeof(AudioStreamBasicDescription)); G("AudioUnitSetProperty(StreamFormat)", e2)
     292        #else
     293        size = sizeof(format);
     294        err = AudioDeviceGetProperty(env.output, 0, 0, kAudioDevicePropertyStreamFormat, &size, &format); G("AudioDeviceGetProperty(StreamFormat)", e0)
     295        if(format.mFormatID != kAudioFormatLinearPCM) err = kAudioDeviceUnsupportedFormatError; G("AudioDeviceGetProperty(StreamFormat)", e0)
     296        #endif
     297       
     298        /* A semaphore can be quite decorative, too. At least the "original"
     299         * semaphore (released at about 0xC0 before Epoch). */
     300       
     301        strcpy(s, "/mpg123-0000");
     302        do
     303        {
     304                for(m = 10;; m--)
     305                        if( (s[m]++) <= '9')
     306                                break;
     307                        else
     308                                s[m] = '0';
     309        } while( (env.semaphore = sem_open(s, O_CREAT | O_EXCL, 0644, 0)) == (sem_t *)SEM_FAILED );
     310       
     311        if(initBuffers() < 0) goto e2;
     312       
     313        /* And prepare audio launching */
     314       
     315        #if MOSX_USES_AUDIOUNIT
     316        #if MOSX_AUDIOUNIT_V2
     317        err = AudioUnitSetProperty(env.output, kAudioUnitProperty_SetRenderCallback, kAudioUnitScope_Input, 0, &inputCallback, sizeof(inputCallback)); G("AudioUnitSetProperty(RenderCallback)", e3)
     318        #else
     319        err = AudioUnitSetProperty(env.output, kAudioUnitProperty_SetInputCallback, kAudioUnitScope_Input, 0, &inputCallback, sizeof(inputCallback)); G("AudioUnitSetProperty(InputCallback)", e3)
     320        #endif
     321        #else
     322        err = AudioDeviceAddIOProc(env.output, playProc, &env); G("AudioDeviceAddIOProc", e3)
     323        #endif
     324       
     325        return(0);
     326       
     327        /* I dream of a programming language where you tell after each operation its
     328         * reverse in case what follows fails. */
     329       
     330        DEB
     331        EB(e3)  destroyBuffers();
     332        #if MOSX_USES_AUDIOUNIT
     333        EB(e2)  AudioUnitUninitialize(env.output);
     334        EB(e1) CloseComponent(env.output);
     335        #else
     336        EB(e2)
     337        #endif
     338        EB(e0) return(-1);
     339        FEB
     340}
     341
     342int audio_reset_parameters(struct audio_info_struct *ai)
     343{
     344        return 0;
     345}
     346
     347int audio_rate_best_match(struct audio_info_struct *ai)
     348{
     349        return 0;
     350}
     351
     352int audio_set_rate(struct audio_info_struct *ai)
     353{
     354        return 0;
     355}
     356
     357int audio_set_channels(struct audio_info_struct *ai)
     358{
     359        return 0;
     360}
     361
     362int audio_set_format(struct audio_info_struct *ai)
     363{
     364        return 0;
     365}
     366
     367int audio_get_formats(struct audio_info_struct *ai)
     368{
     369        return AUDIO_FORMAT_SIGNED_16;
     370}
     371
     372int audio_play_samples(struct audio_info_struct *ai,unsigned char *buf,int len)
     373{
     374        /* We have to calm down mpg123, else he wouldn't hesitate to drop us another
     375         * buffer (which would be the same, in fact) */
     376       
     377        while(sem_wait(env.semaphore)){}        /* We just have to wait a buffer fill request */
     378        fillBuffer(env.to, (short *)buf, len / sizeof(short));
     379        env.to = env.to->next;
     380       
     381        /* And we lauch action if not already done */
     382       
     383        if(!env.play)
     384        {
     385                #if MOSX_USES_AUDIOUNIT
     386                if(AudioOutputUnitStart(env.output)) return(-1);
     387                #else
     388                if(AudioDeviceStart(env.output, playProc)) return(-1);
     389                #endif
     390                env.play = 1;
     391        }
     392       
     393        return len;
     394}
     395
     396int audio_close(struct audio_info_struct *ai)
     397{
     398        #if MOSX_USES_AUDIOUNIT
     399        AudioOutputUnitStop(env.output);
     400        AudioUnitUninitialize(env.output);
     401        CloseComponent(env.output);
     402        #else
     403        AudioDeviceStop(env.output, playProc);  /* No matter the error code, we want to close it (by brute force if necessary) */
     404        AudioDeviceRemoveIOProc(env.output, playProc);
     405        #endif
     406        destroyBuffers();
     407        sem_close(env.semaphore);
     408       
     409        return(0); /* It is often appreciated that closing procedures minimize failure; those who break everything just before leaving are not exactly the kind of people you'll invite again. By returning 0 while blindly ignoring our subprocedures errors, we fake an overpolite guest, which we never tried to be. */
     410}
     411 No newline at end of file