+# ifdef USE_ASYNC_SOUND
+
+/*
+ * Asynchronous sound player revised
+ */
+#if defined(USE_QT_SOUND) && !defined(MACH_O_CARBON)
+# undef USE_QT_SOUND
+#endif /* USE_QT_SOUND && !MACH_O_CARBON */
+
+
+/*
+ * Number of channels in the channel pool
+ */
+#if TARGET_API_MAC_CARBON
+#define MAX_CHANNELS 8
+#else
+#define MAX_CHANNELS 4
+#endif
+
+/*
+ * A pool of sound channels
+ */
+static SndChannelPtr channels[MAX_CHANNELS];
+
+/*
+ * Status of the channel pool
+ */
+static Boolean channel_initialised = FALSE;
+
+/*
+ * Data handles containing sound samples
+ */
+static SndListHandle samples[SOUND_MAX];
+
+/*
+ * Reference counts of sound samples
+ */
+static SInt16 sample_refs[SOUND_MAX];
+
+
+/*
+ * Sound effects
+ *
+ * These constants aren't used by the program at the moment.
+ */
+#define SOUND_VOLUME_MIN 0 /* Default minimum sound volume */
+#define SOUND_VOLUME_MAX 255 /* Default maximum sound volume */
+#define VOLUME_MIN 0 /* Minimum sound volume in % */
+#define VOLUME_MAX 100 /* Maximum sound volume in % */
+#define VOLUME_INC 5 /* Increment sound volume in % */
+
+/* I'm just too lazy to write a panel for this XXX XXX */
+static SInt16 sound_volume = SOUND_VOLUME_MAX;
+
+#ifdef USE_QT_SOUND
+
+/*
+ * QuickTime sound, by Ron Anderson
+ *
+ * I didn't choose to use Windows-style .ini files (Ron wrote a parser
+ * for it, but...), nor did I use lib/xtra directory, hoping someone
+ * would code plist-based configuration code in the future -- pelpel
+ */
+
+/*
+ * (QuickTime)
+ * Load sound effects from data-fork resources. They are wav files
+ * with the same names as angband_sound_name[] (variable.c)
+ *
+ * Globals referenced: angband_sound_name[]
+ * Globals updated: samples[] (they can be *huge*)
+ */
+static void load_sounds(void)
+{
+ OSErr err;
+ int i;
+
+ /* Start QuickTime */
+ err = EnterMovies();
+
+ /* Error */
+ if (err != noErr) return;
+
+ /*
+ * This loop may take a while depending on the count and size of samples
+ * to load.
+ *
+ * We should use a progress dialog for this.
+ */
+ for (i = 1; i < SOUND_MAX; i++)
+ {
+ /* Apple APIs always give me headacke :( */
+ CFStringRef name;
+ FSSpec spec;
+ SInt16 file_id;
+ SInt16 res_id;
+ Str255 movie_name;
+ Movie movie;
+ Track track;
+ Handle h;
+ Boolean res;
+
+ /* Allocate CFString with the name of sound event to be processed */
+ name = CFStringCreateWithCString(NULL, angband_sound_name[i],
+ kTextEncodingUS_ASCII);
+
+ /* Error */
+ if (name == NULL) continue;
+
+ /* Find sound sample resource with the same name */
+ res = get_resource_spec(name, CFSTR("wav"), &spec);
+
+ /* Free the reference to CFString */
+ CFRelease(name);
+
+ /* Error */
+ if (!res) continue;
+
+ /* Open the sound file */
+ err = OpenMovieFile(&spec, &file_id, fsRdPerm);
+
+ /* Error */
+ if (err != noErr) continue;
+
+ /* Create Movie from the file */
+ err = NewMovieFromFile(&movie, file_id, &res_id, movie_name,
+ newMovieActive, NULL);
+
+ /* Error */
+ if (err != noErr) goto close_file;
+
+ /* Get the first track of the movie */
+ track = GetMovieIndTrackType(movie, 1, AudioMediaCharacteristic,
+ movieTrackCharacteristic | movieTrackEnabledOnly );
+
+ /* Error */
+ if (track == NULL) goto close_movie;
+
+ /* Allocate a handle to store sample */
+ h = NewHandle(0);
+
+ /* Error */
+ if (h == NULL) goto close_track;
+
+ /* Dump the sample into the handle */
+ err = PutMovieIntoTypedHandle(movie, track, soundListRsrc, h, 0,
+ GetTrackDuration(track), 0L, NULL);
+
+ /* Success */
+ if (err == noErr)
+ {
+ /* Store the handle in the sample list */
+ samples[i] = (SndListHandle)h;
+ }
+
+ /* Failure */
+ else
+ {
+ /* Free unused handle */
+ DisposeHandle(h);
+ }
+
+ /* Free the track */
+ close_track: DisposeMovieTrack(track);
+
+ /* Free the movie */
+ close_movie: DisposeMovie(movie);
+
+ /* Close the movie file */
+ close_file: CloseMovieFile(file_id);
+ }
+
+ /* Stop QuickTime */
+ ExitMovies();
+}
+
+#else /* USE_QT_SOUND */
+
+/*
+ * Return a handle of 'snd ' resource given Angband sound event number,
+ * or NULL if it isn't found.
+ *
+ * Globals referenced: angband_sound_name[] (variable.c)
+ */
+static SndListHandle find_sound(int num)
+{
+ Str255 sound;
+
+ /* Get the proper sound name */
+ strnfmt((char*)sound + 1, 255, "%.16s.wav", angband_sound_name[num]);
+ sound[0] = strlen((char*)sound + 1);
+
+ /* Obtain resource XXX XXX XXX */
+ return ((SndListHandle)GetNamedResource('snd ', sound));
+}
+
+#endif /* USE_QT_SOUND */
+
+
+/*
+ * Clean up sound support - to be called when the game exits.
+ *
+ * Globals referenced: channels[], samples[], sample_refs[].
+ */
+static void cleanup_sound(void)
+{
+ int i;
+
+ /* No need to clean it up */
+ if (!channel_initialised) return;
+
+ /* Dispose channels */
+ for (i = 0; i < MAX_CHANNELS; i++)
+ {
+ /* Drain sound commands and free the channel */
+ SndDisposeChannel(channels[i], TRUE);
+ }
+
+ /* Free sound data */
+ for (i = 1; i < SOUND_MAX; i++)
+ {
+ /* Still locked */
+ if ((sample_refs[i] > 0) && (samples[i] != NULL))
+ {
+ /* Unlock it */
+ HUnlock((Handle)samples[i]);
+ }
+
+#ifndef USE_QT_SOUND
+
+ /* Release it */
+ if (samples[i]) ReleaseResource((Handle)samples[i]);
+#else
+
+ /* Free handle */
+ if (samples[i]) DisposeHandle((Handle)samples[i]);
+#endif /* !USE_QT_SOUND */
+ }
+}
+
+
+/*
+ * Play sound effects asynchronously -- pelpel
+ *
+ * I don't believe those who first started using the previous implementations
+ * imagined this is *much* more complicated as it may seem. Anyway,
+ * introduced round-robin scheduling of channels and made it much more
+ * paranoid about HLock/HUnlock.
+ *
+ * XXX XXX de-refcounting, HUnlock and ReleaseResource should be done
+ * using channel's callback procedures, which set global flags, and
+ * a procedure hooked into CheckEvents does housekeeping. On the other
+ * hand, this lazy reclaiming strategy keeps things simple (no interrupt
+ * time code) and provides a sort of cache for sound data.
+ *
+ * Globals referenced: channel_initialised, channels[], samples[],
+ * sample_refs[].
+ * Globals updated: ditto.
+ */
+static void play_sound(int num, SInt16 vol)
+{
+ OSErr err;
+ int i;
+ int prev_num;
+ SndListHandle h;
+ SndChannelPtr chan;
+ SCStatus status;
+
+ static int next_chan;
+ static SInt16 channel_occupants[MAX_CHANNELS];
+ static SndCommand volume_cmd, quiet_cmd;
+
+
+ /* Initialise sound channels */
+ if (!channel_initialised)
+ {
+ for (i = 0; i < MAX_CHANNELS; i++)
+ {
+ /* Paranoia - Clear occupant table */
+ /* channel_occupants[i] = 0; */
+
+ /* Create sound channel for all sounds to play from */
+ err = SndNewChannel(&channels[i], sampledSynth, initMono, 0L);
+
+ /* Error */
+ if (err != noErr)
+ {
+ /* Free channels */
+ while (--i >= 0)
+ {
+ SndDisposeChannel(channels[i], TRUE);
+ }
+
+ /* Notify error */
+ plog(_("サウンドチャンネルを初期化出来ません!", "Cannot initialise sound channels!"));
+
+ /* Cancel request */
+ use_sound = arg_sound = FALSE;
+
+ /* Failure */
+ return;
+ }
+ }
+
+ /* First channel to use */
+ next_chan = 0;
+
+ /* Prepare volume command */
+ volume_cmd.cmd = volumeCmd;
+ volume_cmd.param1 = 0;
+ volume_cmd.param2 = 0;
+
+ /* Prepare quiet command */
+ quiet_cmd.cmd = quietCmd;
+ quiet_cmd.param1 = 0;
+ quiet_cmd.param2 = 0;
+
+ /* Initialisation complete */
+ channel_initialised = TRUE;
+ }
+
+ /* Paranoia */
+ if ((num <= 0) || (num >= SOUND_MAX)) return;
+
+ /* Prepare volume command */
+ volume_cmd.param2 = ((SInt32)vol << 16) | vol;
+
+ /* Channel to use (round robin) */
+ chan = channels[next_chan];
+
+ /* See if the resource is already in use */
+ if (sample_refs[num] > 0)
+ {
+ /* Resource in use */
+ h = samples[num];
+
+ /* Increase the refcount */
+ sample_refs[num]++;
+ }
+
+ /* Sound is not currently in use */
+ else
+ {
+ /* Get handle for the sound */
+#ifdef USE_QT_SOUND
+ h = samples[num];
+#else
+ h = find_sound(num);
+#endif /* USE_QT_SOUND */
+
+ /* Sample not available */
+ if (h == NULL) return;
+
+#ifndef USE_QT_SOUND
+
+ /* Load resource */
+ LoadResource((Handle)h);
+
+ /* Remember it */
+ samples[num] = h;
+
+#endif /* !USE_QT_SOUND */
+
+ /* Lock the handle */
+ HLockHi((Handle)h);
+
+ /* Initialise refcount */
+ sample_refs[num] = 1;
+ }
+
+ /* Poll the channel */
+ err = SndChannelStatus(chan, sizeof(SCStatus), &status);
+
+ /* It isn't available */
+ if ((err != noErr) || status.scChannelBusy)
+ {
+ /* Shut it down */
+ SndDoImmediate(chan, &quiet_cmd);
+ }
+
+ /* Previously played sound on this channel */
+ prev_num = channel_occupants[next_chan];
+
+ /* Process previously played sound */
+ if (prev_num != 0)
+ {
+ /* Decrease refcount */
+ sample_refs[prev_num]--;
+
+ /* We can free it now */
+ if (sample_refs[prev_num] <= 0)
+ {
+ /* Unlock */
+ HUnlock((Handle)samples[prev_num]);
+
+#ifndef USE_QT_SOUND
+
+ /* Release */
+ ReleaseResource((Handle)samples[prev_num]);
+
+ /* Forget handle */
+ samples[prev_num] = NULL;
+
+#endif /* !USE_QT_SOUND */
+
+ /* Paranoia */
+ sample_refs[prev_num] = 0;
+ }
+ }
+
+ /* Remember this sound as the current occupant of the channel */
+ channel_occupants[next_chan] = num;
+
+ /* Set up volume for channel */
+ SndDoImmediate(chan, &volume_cmd);
+
+ /* Play new sound asynchronously */
+ SndPlay(chan, h, TRUE);
+
+ /* Schedule next channel (round robin) */
+ next_chan++;
+ if (next_chan >= MAX_CHANNELS) next_chan = 0;
+}
+
+# else /* USE_ASYNC_SOUND */
+
+/*
+ * Play sound synchronously
+ *
+ * This may not be your choice, but much safer and much less resource hungry.
+ */
+static void play_sound(int num, SInt16 vol)
+{
+ Handle handle;
+ Str255 sound;
+
+ /* Get the proper sound name */
+ strnfmt((char*)sound + 1, 255, "%.16s.wav", angband_sound_name[num]);
+ sound[0] = strlen((char*)sound + 1);
+
+ /* Obtain resource XXX XXX XXX */
+ handle = GetNamedResource('snd ', sound);
+
+ /* Oops */
+ if (handle == NULL) return;
+
+ /* Load and Lock */
+ LoadResource(handle);
+ HLockHi(handle);
+
+ /* Play sound (wait for completion) */
+ SndPlay(NULL, (SndListHandle)handle, FALSE);
+
+ /* Unlock and release */
+ HUnlock(handle);
+ ReleaseResource(handle);
+}
+
+# endif /* USE_ASYNC_SOUND */
+
+/*
+ Extra Sound Mode
+*/
+
+
+static short soundmode[8];
+
+#define SND_NON 0
+#define SND_ATTACK 1
+#define SND_MOVE 2
+#define SND_TRAP 3
+#define SND_SHOP 4
+#define SND_ME 5
+#define SND_CMD_ERROR 6
+
+#ifndef MACH_O_CARBON
+
+static int soundchoice[] = {
+ SND_NON,
+ SND_ATTACK,
+ SND_ATTACK,
+ SND_ATTACK,
+ SND_TRAP,
+ SND_ATTACK,
+ SND_ME,
+ SND_ME,
+ SND_ME,
+ SND_MOVE,
+ SND_ATTACK,
+ SND_ME,
+ SND_ATTACK,
+ SND_NON,
+ SND_MOVE,
+ SND_MOVE,
+ SND_ME,
+ SND_SHOP,
+ SND_SHOP,
+ SND_SHOP,
+ SND_SHOP,
+ SND_MOVE,
+ SND_MOVE,
+ SND_MOVE,
+ SND_MOVE,
+ SND_ATTACK,
+ SND_SHOP,
+ SND_SHOP,
+ SND_ME,
+ SND_NON,
+ SND_ATTACK,
+ SND_NON,
+ SND_NON,
+ SND_NON,
+ SND_NON,
+ SND_ATTACK,
+ SND_ATTACK,
+ SND_NON,
+ SND_NON,
+ SND_ATTACK,
+ SND_NON,
+ SND_NON,
+ SND_NON,
+ SND_TRAP,
+ SND_ATTACK,
+ SND_ATTACK,
+ SND_ATTACK,
+ SND_ATTACK,
+ SND_ATTACK,
+ SND_NON,
+ SND_NON,
+ SND_NON,
+ SND_NON,
+ SND_NON,
+ SND_CMD_ERROR,
+ SND_TRAP,
+ SND_NON,
+ SND_NON,
+ SND_TRAP,
+ SND_ATTACK,
+ SND_TRAP,
+ SND_ATTACK,
+ SND_ATTACK,
+ SND_NON,
+ SND_TRAP,
+};
+
+static int ext_sound = 0;
+static int ext_graf = 0;
+
+#endif /* !MACH_O_CARBON */
+
+#endif /* ANGBAND_LITE_MAC */