src/audio/mint/SDL_mintaudio_mcsn.c
author Sam Lantinga
Sat, 19 Sep 2009 13:29:40 +0000
changeset 3280 00cace2d9080
parent 2942 1e431c2631ee
permissions -rw-r--r--
Merged a cleaned up version of Jiang's code changes from Google Summer of Code 2009
     1 /*
     2     SDL - Simple DirectMedia Layer
     3     Copyright (C) 1997-2009 Sam Lantinga
     4 
     5     This library is free software; you can redistribute it and/or
     6     modify it under the terms of the GNU Library General Public
     7     License as published by the Free Software Foundation; either
     8     version 2 of the License, or (at your option) any later version.
     9 
    10     This library is distributed in the hope that it will be useful,
    11     but WITHOUT ANY WARRANTY; without even the implied warranty of
    12     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
    13     Library General Public License for more details.
    14 
    15     You should have received a copy of the GNU Library General Public
    16     License along with this library; if not, write to the Free
    17     Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
    18 
    19     Sam Lantinga
    20     slouken@libsdl.org
    21 */
    22 #include "SDL_config.h"
    23 
    24 /*
    25 	MiNT audio driver
    26 	using XBIOS functions (MacSound compatible driver)
    27 
    28 	Patrice Mandin
    29 */
    30 
    31 #include <support.h>
    32 
    33 /* Mint includes */
    34 #include <mint/osbind.h>
    35 #include <mint/falcon.h>
    36 #include <mint/cookie.h>
    37 
    38 #include "SDL_audio.h"
    39 #include "../SDL_audio_c.h"
    40 #include "../SDL_sysaudio.h"
    41 
    42 #include "../../video/ataricommon/SDL_atarimxalloc_c.h"
    43 
    44 #include "SDL_mintaudio.h"
    45 #include "SDL_mintaudio_mcsn.h"
    46 
    47 /*--- Defines ---*/
    48 
    49 #define MINT_AUDIO_DRIVER_NAME "mint_mcsn"
    50 
    51 /* Debug print info */
    52 #define DEBUG_NAME "audio:mcsn: "
    53 #if 0
    54 #define DEBUG_PRINT(what) \
    55 	{ \
    56 		printf what; \
    57 	}
    58 #else
    59 #define DEBUG_PRINT(what)
    60 #endif
    61 
    62 /*--- Static variables ---*/
    63 
    64 static unsigned long cookie_snd = 0;
    65 static unsigned long cookie_mch = 0;
    66 static cookie_mcsn_t *cookie_mcsn = NULL;
    67 
    68 static void
    69 MINTMCSN_LockDevice(_THIS)
    70 {
    71     /* Stop replay */
    72     Buffoper(0);
    73 }
    74 
    75 static void
    76 MINTMCSN_UnlockDevice(_THIS)
    77 {
    78     /* Restart replay */
    79     Buffoper(SB_PLA_ENA | SB_PLA_RPT);
    80 }
    81 
    82 static void
    83 MINTMCSN_CloseDevice(_THIS)
    84 {
    85     if (this->hidden != NULL) {
    86         /* Stop replay */
    87         SDL_MintAudio_WaitThread();
    88         Buffoper(0);
    89 
    90         if (!SDL_MintAudio_mint_present) {
    91             /* Uninstall interrupt */
    92             Jdisint(MFP_DMASOUND);
    93         }
    94 
    95         /* Wait if currently playing sound */
    96         while (SDL_MintAudio_mutex != 0) {
    97         }
    98 
    99         /* Clear buffers */
   100         if (SDL_MintAudio_audiobuf[0]) {
   101             Mfree(SDL_MintAudio_audiobuf[0]);
   102             SDL_MintAudio_audiobuf[0] = SDL_MintAudio_audiobuf[1] = NULL;
   103         }
   104 
   105         /* Unlock sound system */
   106         Unlocksnd();
   107 
   108         SDL_free(this->hidden);
   109         this->hidden = NULL;
   110     }
   111 }
   112 
   113 static int
   114 MINTMCSN_CheckAudio(_THIS)
   115 {
   116     int i;
   117     unsigned long masterclock, masterprediv;
   118 
   119     DEBUG_PRINT((DEBUG_NAME "asked: %d bits, ",
   120                  SDL_AUDIO_BITSIZE(this->spec.format)));
   121     DEBUG_PRINT(("float=%d, ", SDL_AUDIO_ISFLOAT(this->spec.format)));
   122     DEBUG_PRINT(("signed=%d, ", SDL_AUDIO_ISSIGNED(this->spec.format)));
   123     DEBUG_PRINT(("big endian=%d, ",
   124                  SDL_AUDIO_ISBIGENDIAN(this->spec.format)));
   125     DEBUG_PRINT(("channels=%d, ", this->spec.channels));
   126     DEBUG_PRINT(("freq=%d\n", this->spec.freq));
   127 
   128     if (this->spec.channels > 2) {
   129         this->spec.channels = 2;        /* no more than stereo! */
   130     }
   131 
   132     /* Check formats available */
   133     MINTAUDIO_freqcount = 0;
   134     switch (cookie_mcsn->play) {
   135     case MCSN_ST:
   136         this->spec.channels = 1;
   137         this->spec.format = AUDIO_S8;   /* FIXME: is it signed or unsigned ? */
   138         SDL_MintAudio_AddFrequency(this, 12500, 0, 0, -1);
   139         break;
   140     case MCSN_TT:              /* Also STE, Mega STE */
   141         this->spec.format = AUDIO_S8;
   142         masterclock = MASTERCLOCK_STE;
   143         masterprediv = MASTERPREDIV_STE;
   144         if ((cookie_mch >> 16) == MCH_TT) {
   145             masterclock = MASTERCLOCK_TT;
   146             masterprediv = MASTERPREDIV_TT;
   147         }
   148         for (i = 0; i < 4; i++) {
   149             SDL_MintAudio_AddFrequency(this,
   150                                        masterclock / (masterprediv *
   151                                                       (1 << i)),
   152                                        masterclock, 3 - i, -1);
   153         }
   154         break;
   155     case MCSN_FALCON:          /* Also Mac */
   156         for (i = 1; i < 12; i++) {
   157             /* Remove unusable Falcon codec predivisors */
   158             if ((i == 6) || (i == 8) || (i == 10)) {
   159                 continue;
   160             }
   161             SDL_MintAudio_AddFrequency(this,
   162                                        MASTERCLOCK_FALCON1 /
   163                                        (MASTERPREDIV_FALCON * (i + 1)),
   164                                        CLK25M, i + 1, -1);
   165         }
   166         if (cookie_mcsn->res1 != 0) {
   167             for (i = 1; i < 4; i++) {
   168                 SDL_MintAudio_AddFrequency(this,
   169                                            (cookie_mcsn->res1) /
   170                                            (MASTERPREDIV_FALCON *
   171                                             (1 << i)), CLKEXT,
   172                                            (1 << i) - 1, -1);
   173             }
   174         }
   175         this->spec.format |= SDL_AUDIO_MASK_SIGNED;     /* Audio is always signed */
   176         if ((SDL_AUDIO_BITSIZE(this->spec.format)) == 16) {
   177             this->spec.format |= SDL_AUDIO_MASK_ENDIAN; /* Audio is always big endian */
   178             this->spec.channels = 2;    /* 16 bits always stereo */
   179         }
   180         break;
   181     }
   182 
   183 #if 1
   184     for (i = 0; i < MINTAUDIO_freqcount; i++) {
   185         DEBUG_PRINT((DEBUG_NAME "freq %d: %lu Hz, clock %lu, prediv %d\n",
   186                      i, MINTAUDIO_frequencies[i].frequency,
   187                      MINTAUDIO_frequencies[i].masterclock,
   188                      MINTAUDIO_frequencies[i].predivisor));
   189     }
   190 #endif
   191 
   192     MINTAUDIO_numfreq = SDL_MintAudio_SearchFrequency(this, this->spec.freq);
   193     this->spec.freq = MINTAUDIO_frequencies[MINTAUDIO_numfreq].frequency;
   194 
   195     DEBUG_PRINT((DEBUG_NAME "obtained: %d bits, ",
   196                  SDL_AUDIO_BITSIZE(this->spec.format)));
   197     DEBUG_PRINT(("float=%d, ", SDL_AUDIO_ISFLOAT(this->spec.format)));
   198     DEBUG_PRINT(("signed=%d, ", SDL_AUDIO_ISSIGNED(this->spec.format)));
   199     DEBUG_PRINT(("big endian=%d, ",
   200                  SDL_AUDIO_ISBIGENDIAN(this->spec.format)));
   201     DEBUG_PRINT(("channels=%d, ", this->spec.channels));
   202     DEBUG_PRINT(("freq=%d\n", this->spec.freq));
   203 
   204     return 0;
   205 }
   206 
   207 static void
   208 MINTMCSN_InitAudio(_THIS)
   209 {
   210     int channels_mode, prediv, dmaclock;
   211     void *buffer;
   212 
   213     /* Stop currently playing sound */
   214     SDL_MintAudio_quit_thread = SDL_FALSE;
   215     SDL_MintAudio_thread_finished = SDL_TRUE;
   216     SDL_MintAudio_WaitThread();
   217     Buffoper(0);
   218 
   219     /* Set replay tracks */
   220     Settracks(0, 0);
   221     Setmontracks(0);
   222 
   223     /* Select replay format */
   224     channels_mode = STEREO16;
   225     switch (SDL_AUDIO_BITSIZE(this->spec.format)) {
   226     case 8:
   227         if (this->spec.channels == 2) {
   228             channels_mode = STEREO8;
   229         } else {
   230             channels_mode = MONO8;
   231         }
   232         break;
   233     }
   234     if (Setmode(channels_mode) < 0) {
   235         DEBUG_PRINT((DEBUG_NAME "Setmode() failed\n"));
   236     }
   237 
   238     dmaclock = MINTAUDIO_frequencies[MINTAUDIO_numfreq].masterclock;
   239     prediv = MINTAUDIO_frequencies[MINTAUDIO_numfreq].predivisor;
   240     switch (cookie_mcsn->play) {
   241     case MCSN_TT:
   242         Devconnect(DMAPLAY, DAC, CLK25M, CLKOLD, 1);
   243         Soundcmd(SETPRESCALE, prediv);
   244         DEBUG_PRINT((DEBUG_NAME "STE/TT prescaler selected\n"));
   245         break;
   246     case MCSN_FALCON:
   247         Devconnect(DMAPLAY, DAC, dmaclock, prediv, 1);
   248         DEBUG_PRINT((DEBUG_NAME "Falcon prescaler selected\n"));
   249         break;
   250     }
   251 
   252     /* Set buffer */
   253     buffer = SDL_MintAudio_audiobuf[SDL_MintAudio_numbuf];
   254     if (Setbuffer(0, buffer, buffer + this->spec.size) < 0) {
   255         DEBUG_PRINT((DEBUG_NAME "Setbuffer() failed\n"));
   256     }
   257 
   258     if (SDL_MintAudio_mint_present) {
   259         SDL_MintAudio_thread_pid = tfork(SDL_MintAudio_Thread, 0);
   260     } else {
   261         /* Install interrupt */
   262         Jdisint(MFP_DMASOUND);
   263         Xbtimer(XB_TIMERA, 8, 1, SDL_MintAudio_XbiosInterrupt);
   264         Jenabint(MFP_DMASOUND);
   265 
   266         if (Setinterrupt(SI_TIMERA, SI_PLAY) < 0) {
   267             DEBUG_PRINT((DEBUG_NAME "Setinterrupt() failed\n"));
   268         }
   269     }
   270 
   271     /* Go */
   272     Buffoper(SB_PLA_ENA | SB_PLA_RPT);
   273     DEBUG_PRINT((DEBUG_NAME "hardware initialized\n"));
   274 }
   275 
   276 static int
   277 MINTMCSN_OpenDevice(_THIS, const char *devname, int iscapture)
   278 {
   279     /* Lock sound system */
   280     if (Locksnd() != 1) {
   281         SDL_SetError("MINTMCSN_OpenDevice: Audio system already in use");
   282         return 0;
   283     }
   284 
   285     SDL_MintAudio_device = this;
   286 
   287     /* Check audio capabilities */
   288     if (MINTMCSN_CheckAudio(this) == -1) {
   289         return 0;
   290     }
   291 
   292     /* Initialize all variables that we clean on shutdown */
   293     this->hidden = (struct SDL_PrivateAudioData *)
   294         SDL_malloc((sizeof *this->hidden));
   295     if (this->hidden == NULL) {
   296         SDL_OutOfMemory();
   297         return 0;
   298     }
   299     SDL_memset(this->hidden, 0, (sizeof *this->hidden));
   300 
   301     SDL_CalculateAudioSpec(&this->spec);
   302 
   303     /* Allocate memory for audio buffers in DMA-able RAM */
   304     DEBUG_PRINT((DEBUG_NAME "buffer size=%d\n", this->spec.size));
   305 
   306     SDL_MintAudio_audiobuf[0] =
   307         Atari_SysMalloc(this->spec.size * 2, MX_STRAM);
   308     if (SDL_MintAudio_audiobuf[0] == NULL) {
   309         SDL_free(this->hidden);
   310         this->hidden = NULL;
   311         SDL_OutOfMemory();
   312         return 0;
   313     }
   314     SDL_MintAudio_audiobuf[1] = SDL_MintAudio_audiobuf[0] + this->spec.size;
   315     SDL_MintAudio_numbuf = 0;
   316     SDL_memset(SDL_MintAudio_audiobuf[0], this->spec.silence,
   317                this->spec.size * 2);
   318     SDL_MintAudio_audiosize = this->spec.size;
   319     SDL_MintAudio_mutex = 0;
   320 
   321     DEBUG_PRINT((DEBUG_NAME "buffer 0 at 0x%08x\n",
   322                  SDL_MintAudio_audiobuf[0]));
   323     DEBUG_PRINT((DEBUG_NAME "buffer 1 at 0x%08x\n",
   324                  SDL_MintAudio_audiobuf[1]));
   325 
   326     SDL_MintAudio_CheckFpu();
   327 
   328     /* Setup audio hardware */
   329     MINTMCSN_InitAudio(this);
   330 
   331     return 1;                   /* good to go. */
   332 }
   333 
   334 static int
   335 MINTMCSN_Init(SDL_AudioDriverImpl * impl)
   336 {
   337     unsigned long dummy = 0;
   338 
   339     SDL_MintAudio_mint_present = (Getcookie(C_MiNT, &dummy) == C_FOUND);
   340 
   341     /* We can't use XBIOS in interrupt with Magic, don't know about thread */
   342     if (Getcookie(C_MagX, &dummy) == C_FOUND) {
   343         return 0;
   344     }
   345 
   346     /* Cookie _MCH present ? if not, assume ST machine */
   347     if (Getcookie(C__MCH, &cookie_mch) == C_NOTFOUND) {
   348         cookie_mch = MCH_ST;
   349     }
   350 
   351     /* Cookie _SND present ? if not, assume ST machine */
   352     if (Getcookie(C__SND, &cookie_snd) == C_NOTFOUND) {
   353         cookie_snd = SND_PSG;
   354     }
   355 
   356     /* Check if we have 16 bits audio */
   357     if ((cookie_snd & SND_16BIT) == 0) {
   358         SDL_SetError(DEBUG_NAME "no 16-bit sound");
   359         return 0;
   360     }
   361 
   362     /* Cookie MCSN present ? */
   363     if (Getcookie(C_McSn, (long *) &cookie_mcsn) != C_FOUND) {
   364         SDL_SetError(DEBUG_NAME "no MCSN audio");
   365         return 0;
   366     }
   367 
   368     /* Check if interrupt at end of replay */
   369     if (cookie_mcsn->pint == 0) {
   370         SDL_SetError(DEBUG_NAME "no interrupt at end of replay");
   371         return 0;
   372     }
   373 
   374     /* Check if audio is lockable */
   375     if (Locksnd() != 1) {
   376         SDL_SetError(DEBUG_NAME "audio locked by other application");
   377         return 0;
   378     }
   379 
   380     Unlocksnd();
   381 
   382     DEBUG_PRINT((DEBUG_NAME "MCSN audio available!\n"));
   383 
   384     /* Set the function pointers */
   385     impl->OpenDevice = MINTMCSN_OpenDevice;
   386     impl->CloseDevice = MINTMCSN_CloseDevice;
   387     impl->LockDevice = MINTMCSN_LockDevice;
   388     impl->UnlockDevice = MINTMCSN_UnlockDevice;
   389     impl->OnlyHasDefaultOutputDevice = 1;
   390     impl->ProvidesOwnCallbackThread = 1;
   391     impl->SkipMixerLock = 1;
   392 
   393     return 2;                   /* 2 == definitely has an audio device. */
   394 }
   395 
   396 AudioBootStrap MINTAUDIO_MCSN_bootstrap = {
   397     MINT_AUDIO_DRIVER_NAME, "MiNT MCSN audio driver", MINTMCSN_Init, 0
   398 };
   399 
   400 /* vi: set ts=4 sw=4 expandtab: */