2004-02-10 01:23:53 +03:00
|
|
|
/////////////////////////////////////////////////////////////////////////
|
2011-02-25 01:05:47 +03:00
|
|
|
// $Id$
|
2004-02-10 01:23:53 +03:00
|
|
|
/////////////////////////////////////////////////////////////////////////
|
2009-02-08 12:05:52 +03:00
|
|
|
//
|
2015-01-11 23:13:50 +03:00
|
|
|
// Copyright (C) 2004-2015 The Bochs Project
|
2009-02-08 12:05:52 +03:00
|
|
|
//
|
|
|
|
// This library is free software; you can redistribute it and/or
|
|
|
|
// modify it under the terms of the GNU Lesser General Public
|
|
|
|
// License as published by the Free Software Foundation; either
|
|
|
|
// version 2 of the License, or (at your option) any later version.
|
|
|
|
//
|
|
|
|
// This library is distributed in the hope that it will be useful,
|
|
|
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
|
|
// Lesser General Public License for more details.
|
|
|
|
//
|
|
|
|
// You should have received a copy of the GNU Lesser General Public
|
|
|
|
// License along with this library; if not, write to the Free Software
|
|
|
|
// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
2004-02-10 01:23:53 +03:00
|
|
|
|
|
|
|
// This file (SOUNDOSX.CC) written and donated by Brian Huffman
|
|
|
|
|
|
|
|
#ifdef PARANOID
|
|
|
|
#include <MacTypes.h>
|
|
|
|
#endif
|
2005-12-10 21:37:35 +03:00
|
|
|
|
2011-02-15 00:14:20 +03:00
|
|
|
#include "iodev.h"
|
2014-11-24 21:25:14 +03:00
|
|
|
#include "soundlow.h"
|
2011-08-18 11:05:09 +04:00
|
|
|
#include "soundosx.h"
|
2004-02-10 01:23:53 +03:00
|
|
|
|
2011-03-19 15:57:13 +03:00
|
|
|
#if defined(macintosh) && BX_SUPPORT_SOUNDLOW
|
2005-12-10 21:37:35 +03:00
|
|
|
|
2013-06-24 23:19:12 +04:00
|
|
|
#define LOG_THIS
|
2004-02-10 01:23:53 +03:00
|
|
|
|
|
|
|
#if BX_WITH_MACOS
|
|
|
|
#include <QuickTimeMusic.h>
|
|
|
|
#else
|
2011-04-25 15:22:19 +04:00
|
|
|
#include <CoreServices/CoreServices.h>
|
2004-02-10 01:23:53 +03:00
|
|
|
#include <CoreAudio/CoreAudio.h>
|
|
|
|
#include <AudioUnit/AudioUnit.h>
|
|
|
|
#include <AudioToolbox/DefaultAudioOutput.h>
|
|
|
|
#include <AudioToolbox/AudioConverter.h>
|
|
|
|
#include <AudioToolbox/AUGraph.h>
|
|
|
|
#include <QuickTime/QuickTimeMusic.h>
|
|
|
|
#endif
|
|
|
|
#include <string.h>
|
|
|
|
|
2011-04-25 15:22:19 +04:00
|
|
|
#if (MAC_OS_X_VERSION_MAX_ALLOWED >= 1030)
|
|
|
|
#define BX_SOUND_OSX_CONVERTER_NEW_API 1
|
|
|
|
#endif
|
|
|
|
|
2004-02-10 01:23:53 +03:00
|
|
|
#ifdef BX_SOUND_OSX_use_converter
|
2011-04-25 15:22:19 +04:00
|
|
|
#ifndef BX_SOUND_OSX_CONVERTER_NEW_API
|
|
|
|
OSStatus MyRenderer (void *inRefCon, AudioUnitRenderActionFlags inActionFlags, const AudioTimeStamp *inTimeStamp, UInt32 inBusNumber, AudioBuffer *ioData);
|
|
|
|
OSStatus MyACInputProc (AudioConverterRef inAudioConverter, UInt32* outDataSize, void** outData, void* inUserData);
|
|
|
|
#else
|
|
|
|
OSStatus MyRenderer (void *inRefCon, AudioUnitRenderActionFlags *ioActionFlags, const AudioTimeStamp *inTimeStamp, UInt32 inBusNumber, UInt32 inNumberFrames, AudioBufferList *ioData);
|
|
|
|
OSStatus MyACInputProc (AudioConverterRef inAudioConverter, UInt32 *ioNumberDataPackets, AudioBufferList *ioData, AudioStreamPacketDescription **outDataPacketDescription, void *inUserData);
|
|
|
|
#endif
|
2004-02-10 01:23:53 +03:00
|
|
|
#endif
|
|
|
|
|
|
|
|
// Global variables
|
|
|
|
#ifdef BX_SOUND_OSX_use_converter
|
|
|
|
AUGraph MidiGraph;
|
|
|
|
AudioUnit synthUnit;
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#ifdef BX_SOUND_OSX_use_quicktime
|
|
|
|
SndChannelPtr WaveChannel;
|
|
|
|
ExtSoundHeader WaveInfo;
|
|
|
|
ExtSoundHeader WaveHeader[BX_SOUND_OSX_NBUF];
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#ifdef BX_SOUND_OSX_use_converter
|
|
|
|
AudioUnit WaveOutputUnit = NULL;
|
|
|
|
AudioConverterRef WaveConverter = NULL;
|
|
|
|
#endif
|
|
|
|
|
2013-06-24 23:19:12 +04:00
|
|
|
bx_sound_osx_c::bx_sound_osx_c()
|
|
|
|
:bx_sound_lowlevel_c()
|
2004-02-10 01:23:53 +03:00
|
|
|
{
|
|
|
|
MidiOpen = 0;
|
|
|
|
WaveOpen = 0;
|
|
|
|
head = 0;
|
|
|
|
tail = 0;
|
|
|
|
for (int i=0; i<BX_SOUND_OSX_NBUF; i++)
|
|
|
|
WaveLength[i] = 0;
|
2013-07-25 22:47:11 +04:00
|
|
|
BX_INFO(("Sound lowlevel module 'osx' initialized"));
|
2004-02-10 01:23:53 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
bx_sound_osx_c::~bx_sound_osx_c()
|
|
|
|
{
|
|
|
|
// nothing for now
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
int bx_sound_osx_c::midiready()
|
|
|
|
{
|
2011-03-20 21:02:12 +03:00
|
|
|
return BX_SOUNDLOW_OK;
|
2004-02-10 01:23:53 +03:00
|
|
|
}
|
|
|
|
|
2011-02-13 20:26:52 +03:00
|
|
|
int bx_sound_osx_c::openmidioutput(const char *mididev)
|
2004-02-10 01:23:53 +03:00
|
|
|
{
|
|
|
|
#ifdef BX_SOUND_OSX_use_converter
|
|
|
|
ComponentDescription description;
|
|
|
|
AUNode synthNode, outputNode;
|
|
|
|
|
|
|
|
// Create the graph
|
|
|
|
NewAUGraph (&MidiGraph);
|
|
|
|
|
|
|
|
// Open the DLS Synth
|
2011-04-25 15:22:19 +04:00
|
|
|
description.componentType = kAudioUnitType_MusicDevice;
|
|
|
|
description.componentSubType = kAudioUnitSubType_DLSSynth;
|
|
|
|
description.componentManufacturer = kAudioUnitManufacturer_Apple;
|
2004-02-10 01:23:53 +03:00
|
|
|
description.componentFlags = 0;
|
|
|
|
description.componentFlagsMask = 0;
|
|
|
|
AUGraphNewNode (MidiGraph, &description, 0, NULL, &synthNode);
|
|
|
|
|
|
|
|
// Open the output device
|
2011-04-25 15:22:19 +04:00
|
|
|
description.componentType = kAudioUnitType_Output;
|
|
|
|
description.componentSubType = kAudioUnitSubType_DefaultOutput;
|
|
|
|
description.componentManufacturer = kAudioUnitManufacturer_Apple;
|
2004-02-10 01:23:53 +03:00
|
|
|
description.componentFlags = 0;
|
|
|
|
description.componentFlagsMask = 0;
|
|
|
|
AUGraphNewNode (MidiGraph, &description, 0, NULL, &outputNode);
|
|
|
|
|
|
|
|
// Connect the devices up
|
|
|
|
AUGraphConnectNodeInput (MidiGraph, synthNode, 1, outputNode, 0);
|
|
|
|
AUGraphUpdate (MidiGraph, NULL);
|
2008-01-27 01:24:03 +03:00
|
|
|
|
2004-02-10 01:23:53 +03:00
|
|
|
// Open and initialize the audio units
|
|
|
|
AUGraphOpen (MidiGraph);
|
|
|
|
AUGraphInitialize (MidiGraph);
|
|
|
|
|
|
|
|
// Turn off the reverb on the synth
|
|
|
|
AUGraphGetNodeInfo (MidiGraph, synthNode, NULL, NULL, NULL, &synthUnit);
|
|
|
|
UInt32 usesReverb = 0;
|
|
|
|
AudioUnitSetProperty (synthUnit, kMusicDeviceProperty_UsesInternalReverb,
|
|
|
|
kAudioUnitScope_Global, 0, &usesReverb, sizeof (usesReverb));
|
|
|
|
|
|
|
|
// Start playing
|
|
|
|
AUGraphStart (MidiGraph);
|
|
|
|
#endif
|
2011-02-11 01:58:22 +03:00
|
|
|
BX_DEBUG(("openmidioutput(%s)", mididev));
|
2004-02-10 01:23:53 +03:00
|
|
|
MidiOpen = 1;
|
2011-03-20 21:02:12 +03:00
|
|
|
return BX_SOUNDLOW_OK;
|
2004-02-10 01:23:53 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
int bx_sound_osx_c::sendmidicommand(int delta, int command, int length, Bit8u data[])
|
|
|
|
{
|
2011-02-11 01:58:22 +03:00
|
|
|
BX_DEBUG(("sendmidicommand(%i,%02x,%i)", delta, command, length));
|
2011-03-20 21:02:12 +03:00
|
|
|
if (!MidiOpen) return BX_SOUNDLOW_ERR;
|
2004-02-10 01:23:53 +03:00
|
|
|
|
|
|
|
#ifdef BX_SOUND_OSX_use_converter
|
|
|
|
if (length <= 2) {
|
|
|
|
Bit8u arg1 = (length >=1) ? data[0] : 0;
|
|
|
|
Bit8u arg2 = (length >=2) ? data[1] : 0;
|
|
|
|
MusicDeviceMIDIEvent (synthUnit, command, arg1, arg2, delta);
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
MusicDeviceSysEx (synthUnit, data, length);
|
|
|
|
}
|
|
|
|
#endif
|
2011-03-20 21:02:12 +03:00
|
|
|
return BX_SOUNDLOW_OK;
|
2004-02-10 01:23:53 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
int bx_sound_osx_c::closemidioutput()
|
|
|
|
{
|
2011-02-11 01:58:22 +03:00
|
|
|
BX_DEBUG(("closemidioutput()"));
|
2004-02-10 01:23:53 +03:00
|
|
|
MidiOpen = 0;
|
|
|
|
#ifdef BX_SOUND_OSX_use_converter
|
|
|
|
AUGraphStop (MidiGraph);
|
|
|
|
AUGraphClose (MidiGraph);
|
|
|
|
#endif
|
2011-03-20 21:02:12 +03:00
|
|
|
return BX_SOUNDLOW_OK;
|
2004-02-10 01:23:53 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
#ifdef BX_SOUND_OSX_use_quicktime
|
|
|
|
#if BX_WITH_MACOS
|
|
|
|
pascal
|
|
|
|
#endif
|
|
|
|
void WaveCallbackProc (SndChannelPtr chan, SndCommand *cmd)
|
|
|
|
{
|
|
|
|
// a new buffer is available, so increment tail pointer
|
|
|
|
int *tail = (int *) (cmd->param2);
|
|
|
|
(*tail)++;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2011-02-13 20:26:52 +03:00
|
|
|
int bx_sound_osx_c::openwaveoutput(const char *wavedev)
|
2004-02-10 01:23:53 +03:00
|
|
|
{
|
|
|
|
OSStatus err;
|
|
|
|
|
2011-02-11 01:58:22 +03:00
|
|
|
BX_DEBUG(("openwaveoutput(%s)", wavedev));
|
2004-02-10 01:23:53 +03:00
|
|
|
|
|
|
|
// open the default output unit
|
|
|
|
#ifdef BX_SOUND_OSX_use_quicktime
|
|
|
|
err = SndNewChannel (&WaveChannel, sampledSynth, 0, NewSndCallBackUPP(WaveCallbackProc));
|
2011-03-20 21:02:12 +03:00
|
|
|
if (err != noErr) return BX_SOUNDLOW_ERR;
|
2004-02-10 01:23:53 +03:00
|
|
|
#endif
|
|
|
|
#ifdef BX_SOUND_OSX_use_converter
|
2011-04-25 15:22:19 +04:00
|
|
|
#ifndef BX_SOUND_OSX_CONVERTER_NEW_API
|
2004-02-10 01:23:53 +03:00
|
|
|
err = OpenDefaultAudioOutput (&WaveOutputUnit);
|
2011-03-20 21:02:12 +03:00
|
|
|
if (err != noErr) return BX_SOUNDLOW_ERR;
|
2004-02-10 01:23:53 +03:00
|
|
|
|
2004-02-18 21:54:21 +03:00
|
|
|
AudioUnitInputCallback input;
|
|
|
|
input.inputProc = MyRenderer;
|
|
|
|
input.inputProcRefCon = (void *) this;
|
2008-01-27 01:24:03 +03:00
|
|
|
AudioUnitSetProperty (WaveOutputUnit, kAudioUnitProperty_SetInputCallback,
|
2004-02-10 01:23:53 +03:00
|
|
|
kAudioUnitScope_Global, 0, &input, sizeof(input));
|
2011-04-25 15:22:19 +04:00
|
|
|
#else
|
|
|
|
ComponentDescription desc;
|
|
|
|
desc.componentType = kAudioUnitType_Output;
|
|
|
|
desc.componentSubType = kAudioUnitSubType_DefaultOutput;
|
|
|
|
desc.componentManufacturer = kAudioUnitManufacturer_Apple;
|
|
|
|
desc.componentFlags = 0;
|
|
|
|
desc.componentFlagsMask = 0;
|
|
|
|
|
|
|
|
Component c = FindNextComponent(NULL, &desc);
|
|
|
|
if (c == NULL) {
|
|
|
|
BX_ERROR(("Core Audio: Unable to find default audio output component\n"));
|
|
|
|
return BX_SOUNDLOW_ERR;
|
|
|
|
}
|
|
|
|
err = OpenAComponent(c, &WaveOutputUnit);
|
|
|
|
if (err) {
|
|
|
|
BX_ERROR(("Core Audio: Unable to open audio output (err=%X)\n", (unsigned int)err));
|
|
|
|
return BX_SOUNDLOW_ERR;
|
|
|
|
}
|
|
|
|
|
|
|
|
AURenderCallbackStruct input;
|
|
|
|
input.inputProc = MyRenderer;
|
|
|
|
input.inputProcRefCon = (void *) this;
|
|
|
|
err = AudioUnitSetProperty (WaveOutputUnit,
|
|
|
|
kAudioUnitProperty_SetRenderCallback,
|
|
|
|
kAudioUnitScope_Input, 0, &input, sizeof(input));
|
|
|
|
if (err) {
|
|
|
|
BX_ERROR(("Core Audio: AudioUnitSetProperty error (err=%X)\n", (unsigned int)err));
|
|
|
|
return BX_SOUNDLOW_ERR;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
err = AudioUnitInitialize (WaveOutputUnit);
|
|
|
|
if (err) {
|
|
|
|
BX_ERROR(("Core Audio: AudioUnitInitialize error (err=%X)\n", (unsigned int)err));
|
|
|
|
return BX_SOUNDLOW_ERR;
|
|
|
|
}
|
2004-02-10 01:23:53 +03:00
|
|
|
#endif
|
|
|
|
|
|
|
|
WaveOpen = 1;
|
2011-03-20 21:02:12 +03:00
|
|
|
return BX_SOUNDLOW_OK;
|
2004-02-10 01:23:53 +03:00
|
|
|
}
|
|
|
|
|
2011-04-25 15:22:19 +04:00
|
|
|
#ifdef BX_SOUND_OSX_use_converter
|
|
|
|
OSStatus bx_sound_osx_c::core_audio_pause() {
|
|
|
|
OSStatus err = noErr;
|
|
|
|
|
|
|
|
if (WaveOutputUnit) {
|
|
|
|
err = AudioOutputUnitStop (WaveOutputUnit);
|
|
|
|
if (err) {
|
|
|
|
BX_ERROR(("Core Audio: nextbuffer(): AudioOutputUnitStop (err=%X)\n", (unsigned int)err));
|
|
|
|
}
|
|
|
|
WavePlaying = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
|
|
|
OSStatus bx_sound_osx_c::core_audio_resume() {
|
|
|
|
OSStatus err = noErr;
|
|
|
|
|
|
|
|
if (WaveConverter) {
|
|
|
|
err = AudioConverterReset (WaveConverter);
|
|
|
|
if (err) {
|
|
|
|
BX_ERROR(("Core Audio: core_audio_resume(): AudioConverterReset (err=%X)\n", (unsigned int)err));
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (WaveOutputUnit) {
|
|
|
|
err = AudioOutputUnitStart (WaveOutputUnit);
|
|
|
|
if (err) {
|
|
|
|
BX_ERROR(("Core Audio: core_audio_resume(): AudioOutputUnitStart (err=%X)\n", (unsigned int)err));
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
WavePlaying = 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2011-03-20 21:02:12 +03:00
|
|
|
int bx_sound_osx_c::startwaveplayback(int frequency, int bits, bx_bool stereo, int format)
|
2004-02-10 01:23:53 +03:00
|
|
|
{
|
|
|
|
#ifdef BX_SOUND_OSX_use_converter
|
2011-04-25 15:22:19 +04:00
|
|
|
static int oldfreq, oldbits, oldformat;
|
|
|
|
static bx_bool oldstereo;
|
2004-02-10 01:23:53 +03:00
|
|
|
AudioStreamBasicDescription srcFormat, dstFormat;
|
|
|
|
UInt32 formatSize = sizeof(AudioStreamBasicDescription);
|
2011-04-25 15:22:19 +04:00
|
|
|
OSStatus err;
|
2004-02-10 01:23:53 +03:00
|
|
|
#endif
|
|
|
|
|
2011-02-11 01:58:22 +03:00
|
|
|
BX_DEBUG(("startwaveplayback(%d, %d, %d, %x)", frequency, bits, stereo, format));
|
2004-02-10 01:23:53 +03:00
|
|
|
|
|
|
|
#ifdef BX_SOUND_OSX_use_quicktime
|
|
|
|
WaveInfo.samplePtr = NULL;
|
|
|
|
WaveInfo.numChannels = stereo ? 2 : 1;
|
|
|
|
WaveInfo.sampleRate = frequency << 16; // sampleRate is a 16.16 fixed-point value
|
|
|
|
WaveInfo.loopStart = 0;
|
|
|
|
WaveInfo.loopEnd = 0;
|
|
|
|
WaveInfo.encode = extSH; // WaveInfo has type ExtSoundHeader
|
|
|
|
WaveInfo.baseFrequency = 1; // not sure what means. It's only a Uint8.
|
|
|
|
WaveInfo.numFrames = 0;
|
|
|
|
//WaveInfo.AIFFSampleRate = frequency; // frequency as float80
|
|
|
|
WaveInfo.markerChunk = NULL;
|
|
|
|
|
|
|
|
WaveInfo.instrumentChunks = NULL;
|
|
|
|
WaveInfo.AESRecording = NULL;
|
|
|
|
WaveInfo.sampleSize = bits * WaveInfo.numChannels;
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#ifdef BX_SOUND_OSX_use_converter
|
2008-02-16 01:05:43 +03:00
|
|
|
if ((frequency == oldfreq) &&
|
2008-01-27 01:24:03 +03:00
|
|
|
(bits == oldbits) &&
|
2004-02-10 01:23:53 +03:00
|
|
|
(stereo == oldstereo) &&
|
2008-02-16 01:05:43 +03:00
|
|
|
(format == oldformat))
|
2011-04-25 15:22:19 +04:00
|
|
|
return BX_SOUNDLOW_OK; // nothing to do
|
2004-02-10 01:23:53 +03:00
|
|
|
|
|
|
|
oldfreq = frequency;
|
|
|
|
oldbits = bits;
|
|
|
|
oldstereo = stereo;
|
|
|
|
oldformat = format;
|
|
|
|
|
|
|
|
// update the source audio format
|
|
|
|
UInt32 bytes = bits / 8;
|
|
|
|
UInt32 channels = stereo ? 2 : 1;
|
|
|
|
srcFormat.mSampleRate = (Float64) frequency;
|
|
|
|
srcFormat.mFormatID = kAudioFormatLinearPCM;
|
|
|
|
srcFormat.mFormatFlags = kLinearPCMFormatFlagIsPacked;
|
|
|
|
if (format & 1) srcFormat.mFormatFlags |= kLinearPCMFormatFlagIsSignedInteger;
|
|
|
|
srcFormat.mBytesPerPacket = channels * bytes;
|
|
|
|
srcFormat.mFramesPerPacket = 1;
|
|
|
|
srcFormat.mBytesPerFrame = channels * bytes;
|
|
|
|
srcFormat.mChannelsPerFrame = channels;
|
|
|
|
srcFormat.mBitsPerChannel = bytes * 8;
|
|
|
|
|
2011-04-25 15:22:19 +04:00
|
|
|
if (WavePlaying) {
|
|
|
|
err = AudioOutputUnitStop (WaveOutputUnit);
|
|
|
|
if (err)
|
|
|
|
BX_ERROR(("Core Audio: startwaveplayback(): AudioOutputUnitStop (err=%X)\n", (unsigned int)err));
|
|
|
|
}
|
|
|
|
if (WaveConverter) {
|
|
|
|
err = AudioConverterDispose (WaveConverter);
|
|
|
|
if (err)
|
|
|
|
BX_ERROR(("Core Audio: startwaveplayback(): AudioConverterDispose (err=%X)\n", (unsigned int)err));
|
|
|
|
}
|
2004-02-10 01:23:53 +03:00
|
|
|
|
2011-04-25 15:22:19 +04:00
|
|
|
err = AudioUnitGetProperty (WaveOutputUnit, kAudioUnitProperty_StreamFormat,
|
2004-02-10 01:23:53 +03:00
|
|
|
kAudioUnitScope_Output, 0, &dstFormat, &formatSize);
|
2011-04-25 15:22:19 +04:00
|
|
|
if (err) {
|
|
|
|
BX_ERROR(("Core Audio: startwaveplayback(): AudioUnitGetProperty (err=%X)\n", (unsigned int)err));
|
|
|
|
return BX_SOUNDLOW_ERR;
|
|
|
|
}
|
|
|
|
|
|
|
|
#ifdef BX_SOUND_OSX_CONVERTER_NEW_API
|
|
|
|
// force interleaved mode
|
|
|
|
dstFormat.mFormatFlags |= kAudioFormatFlagIsNonInterleaved;
|
|
|
|
dstFormat.mBytesPerPacket = dstFormat.mBytesPerFrame = (dstFormat.mBitsPerChannel + 7) / 8;
|
|
|
|
err = AudioUnitSetProperty (WaveOutputUnit, kAudioUnitProperty_StreamFormat,
|
|
|
|
kAudioUnitScope_Input, 0, &dstFormat, sizeof(dstFormat));
|
|
|
|
if (err) {
|
|
|
|
BX_ERROR(("Core Audio: startwaveplayback(): AudioUnitSetProperty (err=%X)\n", (unsigned int)err));
|
|
|
|
return BX_SOUNDLOW_ERR;
|
|
|
|
}
|
|
|
|
#endif
|
2004-02-10 01:23:53 +03:00
|
|
|
|
2011-04-25 15:22:19 +04:00
|
|
|
err = AudioConverterNew (&srcFormat, &dstFormat, &WaveConverter);
|
|
|
|
if (err) {
|
|
|
|
BX_ERROR(("Core Audio: startwaveplayback(): AudioConverterNew (err=%X)\n", (unsigned int)err));
|
|
|
|
return BX_SOUNDLOW_ERR;
|
|
|
|
}
|
2004-02-10 01:23:53 +03:00
|
|
|
|
|
|
|
if (srcFormat.mChannelsPerFrame == 1 && dstFormat.mChannelsPerFrame == 2) {
|
|
|
|
// map single-channel input to both output channels
|
|
|
|
SInt32 map[2] = {0,0};
|
2011-04-25 15:22:19 +04:00
|
|
|
err = AudioConverterSetProperty (WaveConverter,
|
|
|
|
kAudioConverterChannelMap,
|
2004-02-10 01:23:53 +03:00
|
|
|
sizeof(map), (void*) map);
|
2011-04-25 15:22:19 +04:00
|
|
|
if (err) {
|
|
|
|
BX_ERROR(("Core Audio: startwaveplayback(): AudioConverterSetProperty (err=%X)\n", (unsigned int)err));
|
|
|
|
return BX_SOUNDLOW_ERR;
|
|
|
|
}
|
2004-02-10 01:23:53 +03:00
|
|
|
}
|
|
|
|
|
2011-04-25 15:22:19 +04:00
|
|
|
if (WavePlaying) {
|
|
|
|
if (core_audio_resume() != noErr)
|
|
|
|
return BX_SOUNDLOW_ERR;
|
|
|
|
}
|
2004-02-10 01:23:53 +03:00
|
|
|
#endif
|
|
|
|
|
2011-03-20 21:02:12 +03:00
|
|
|
return BX_SOUNDLOW_OK;
|
2004-02-10 01:23:53 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
int bx_sound_osx_c::waveready()
|
|
|
|
{
|
|
|
|
// HACK: the -4 is to keep from overwriting buffers that
|
|
|
|
// have been sent, but possibly not yet played. There
|
|
|
|
// should be a better way of doing this.
|
|
|
|
if (WaveOpen && (head - tail < BX_SOUND_OSX_NBUF-4)) {
|
2011-03-20 21:02:12 +03:00
|
|
|
return BX_SOUNDLOW_OK;
|
2004-02-10 01:23:53 +03:00
|
|
|
}
|
|
|
|
else {
|
|
|
|
#ifdef BX_SOUND_OSX_use_converter
|
|
|
|
// If buffer is full, make sure sound is playing
|
2011-04-25 15:22:19 +04:00
|
|
|
if (!WavePlaying) {
|
|
|
|
if (core_audio_resume() != noErr)
|
|
|
|
return BX_SOUNDLOW_ERR;
|
2004-02-10 01:23:53 +03:00
|
|
|
}
|
|
|
|
#endif
|
2011-03-20 21:02:12 +03:00
|
|
|
return BX_SOUNDLOW_ERR;
|
2004-02-10 01:23:53 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-01-11 23:13:50 +03:00
|
|
|
int bx_sound_osx_c::sendwavepacket(int length, Bit8u data[], bx_pcm_param_t *param)
|
2004-02-10 01:23:53 +03:00
|
|
|
{
|
|
|
|
#ifdef BX_SOUND_OSX_use_quicktime
|
2015-01-11 23:13:50 +03:00
|
|
|
SndCommand mySndCommand;
|
2004-02-10 01:23:53 +03:00
|
|
|
#endif
|
|
|
|
|
2015-01-11 23:13:50 +03:00
|
|
|
BX_DEBUG(("sendwavepacket(%d, %p), head=%u", length, data, head));
|
|
|
|
|
|
|
|
if (memcmp(param, &pcm_param, sizeof(bx_pcm_param_t)) != 0) {
|
|
|
|
startwaveplayback(param->samplerate, param->bits, param->channels == 2,
|
|
|
|
param->format);
|
|
|
|
pcm_param = *param;
|
|
|
|
}
|
2004-02-10 01:23:53 +03:00
|
|
|
|
|
|
|
// sanity check
|
|
|
|
if ((!WaveOpen) || (head - tail >= BX_SOUND_OSX_NBUF))
|
2011-03-20 21:02:12 +03:00
|
|
|
return BX_SOUNDLOW_ERR;
|
2004-02-10 01:23:53 +03:00
|
|
|
|
|
|
|
// find next available buffer
|
|
|
|
int n = head++ % BX_SOUND_OSX_NBUF;
|
|
|
|
|
|
|
|
// put data in buffer
|
|
|
|
memcpy(WaveData[n], data, length);
|
|
|
|
WaveLength[n] = length;
|
|
|
|
|
|
|
|
#ifdef BX_SOUND_OSX_use_quicktime
|
|
|
|
memcpy(&WaveHeader[n], &WaveInfo, sizeof(WaveInfo));
|
|
|
|
WaveHeader[n].samplePtr = (char *) (WaveData[n]);
|
|
|
|
WaveHeader[n].numFrames = length * 8 / WaveInfo.sampleSize;
|
|
|
|
#endif
|
|
|
|
#ifdef BX_SOUND_OSX_use_converter
|
|
|
|
// make sure that the sound is playing
|
|
|
|
if (!WavePlaying) {
|
2011-04-25 15:22:19 +04:00
|
|
|
if (core_audio_resume() != noErr)
|
|
|
|
return BX_SOUNDLOW_ERR;
|
2004-02-10 01:23:53 +03:00
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#ifdef BX_SOUND_OSX_use_quicktime
|
|
|
|
// queue buffer to play
|
|
|
|
mySndCommand.cmd = bufferCmd;
|
|
|
|
mySndCommand.param1 = 0;
|
|
|
|
mySndCommand.param2 = (long)(&WaveHeader[n]);
|
|
|
|
SndDoCommand(WaveChannel, &mySndCommand, TRUE);
|
|
|
|
|
|
|
|
// queue callback for when buffer finishes
|
|
|
|
mySndCommand.cmd = callBackCmd;
|
|
|
|
mySndCommand.param1 = 0;
|
|
|
|
mySndCommand.param2 = (long)(&tail);
|
|
|
|
SndDoCommand(WaveChannel, &mySndCommand, TRUE);
|
|
|
|
#endif
|
|
|
|
|
2011-03-20 21:02:12 +03:00
|
|
|
return BX_SOUNDLOW_OK;
|
2004-02-10 01:23:53 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
int bx_sound_osx_c::stopwaveplayback()
|
|
|
|
{
|
2011-03-20 21:02:12 +03:00
|
|
|
return BX_SOUNDLOW_OK;
|
2004-02-10 01:23:53 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
int bx_sound_osx_c::closewaveoutput()
|
|
|
|
{
|
|
|
|
#ifdef BX_SOUND_OSX_use_converter
|
|
|
|
if (WavePlaying) AudioOutputUnitStop (WaveOutputUnit);
|
|
|
|
if (WaveConverter) AudioConverterDispose (WaveConverter);
|
|
|
|
if (WaveOutputUnit) CloseComponent (WaveOutputUnit);
|
|
|
|
WavePlaying = 0;
|
|
|
|
WaveOpen = 0;
|
|
|
|
WaveConverter = NULL;
|
|
|
|
WaveOutputUnit = NULL;
|
|
|
|
#endif
|
2011-03-20 21:02:12 +03:00
|
|
|
return BX_SOUNDLOW_OK;
|
2004-02-10 01:23:53 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
#ifdef BX_SOUND_OSX_use_converter
|
2011-04-25 15:22:19 +04:00
|
|
|
#ifndef BX_SOUND_OSX_CONVERTER_NEW_API
|
2004-02-10 01:23:53 +03:00
|
|
|
OSStatus MyRenderer (void *inRefCon, AudioUnitRenderActionFlags inActionFlags,
|
|
|
|
const AudioTimeStamp *inTimeStamp, UInt32 inBusNumber, AudioBuffer *ioData)
|
|
|
|
{
|
2011-04-25 15:22:19 +04:00
|
|
|
OSStatus err;
|
2004-02-10 01:23:53 +03:00
|
|
|
UInt32 size = ioData->mDataByteSize;
|
2011-04-25 15:22:19 +04:00
|
|
|
err = AudioConverterFillBuffer (WaveConverter, MyACInputProc, inRefCon, &size, ioData->mData);
|
|
|
|
return err;
|
2004-02-10 01:23:53 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
OSStatus MyACInputProc (AudioConverterRef inAudioConverter,
|
|
|
|
UInt32* outDataSize, void** outData, void* inUserData)
|
|
|
|
{
|
|
|
|
bx_sound_osx_c *self = (bx_sound_osx_c*) inUserData;
|
|
|
|
self->nextbuffer ((int*) outDataSize, outData);
|
|
|
|
return noErr;
|
|
|
|
}
|
2011-04-25 15:22:19 +04:00
|
|
|
#else
|
|
|
|
OSStatus MyRenderer (void *inRefCon, AudioUnitRenderActionFlags *ioActionFlags, const AudioTimeStamp *inTimeStamp, UInt32 inBusNumber, UInt32 inNumberFrames, AudioBufferList *ioData)
|
|
|
|
{
|
|
|
|
UInt32 packets;
|
|
|
|
AudioStreamBasicDescription dstFormat;
|
|
|
|
UInt32 formatSize = sizeof(AudioStreamBasicDescription);
|
|
|
|
OSStatus err = noErr;
|
|
|
|
|
|
|
|
err = AudioConverterGetProperty (WaveConverter,
|
|
|
|
kAudioConverterCurrentOutputStreamDescription,
|
|
|
|
&formatSize, &dstFormat);
|
|
|
|
if (err) {
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
|
|
|
packets = inNumberFrames / dstFormat.mFramesPerPacket;
|
|
|
|
err = AudioConverterFillComplexBuffer(WaveConverter,
|
|
|
|
MyACInputProc, inRefCon, &packets, ioData, NULL);
|
|
|
|
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
|
|
|
OSStatus MyACInputProc (AudioConverterRef inAudioConverter, UInt32 *ioNumberDataPackets, AudioBufferList *ioData, AudioStreamPacketDescription **outDataPacketDescription, void *inUserData)
|
|
|
|
{
|
|
|
|
OSStatus err;
|
|
|
|
bx_sound_osx_c *self = (bx_sound_osx_c*) inUserData;
|
|
|
|
AudioStreamBasicDescription srcFormat;
|
|
|
|
UInt32 formatSize = sizeof(AudioStreamBasicDescription);
|
|
|
|
|
|
|
|
err = AudioConverterGetProperty (inAudioConverter,
|
|
|
|
kAudioConverterCurrentInputStreamDescription,
|
|
|
|
&formatSize, &srcFormat);
|
|
|
|
if (err) {
|
|
|
|
*ioNumberDataPackets = 0;
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
int outDataSize = *ioNumberDataPackets * srcFormat.mBytesPerPacket;
|
|
|
|
void *outData = ioData->mBuffers[0].mData;
|
|
|
|
self->nextbuffer ((int*) &outDataSize, &outData);
|
|
|
|
*ioNumberDataPackets = outDataSize / srcFormat.mBytesPerPacket;
|
|
|
|
ioData->mBuffers[0].mDataByteSize = outDataSize;
|
|
|
|
ioData->mBuffers[0].mData = outData;
|
|
|
|
|
|
|
|
return noErr;
|
|
|
|
}
|
|
|
|
#endif
|
2004-02-10 01:23:53 +03:00
|
|
|
|
|
|
|
void bx_sound_osx_c::nextbuffer (int *outDataSize, void **outData)
|
|
|
|
{
|
2011-02-11 01:58:22 +03:00
|
|
|
BX_DEBUG(("nextbuffer(), tail=%u", tail));
|
2004-02-10 01:23:53 +03:00
|
|
|
if (head - tail <= 0) {
|
|
|
|
*outData = NULL;
|
|
|
|
*outDataSize = 0;
|
|
|
|
|
|
|
|
// We are getting behind, so stop the output for now
|
2011-04-25 15:22:19 +04:00
|
|
|
core_audio_pause();
|
2004-02-10 01:23:53 +03:00
|
|
|
}
|
|
|
|
else {
|
|
|
|
int n = tail % BX_SOUND_OSX_NBUF;
|
|
|
|
*outData = (void *) (WaveData[n]);
|
|
|
|
*outDataSize = WaveLength[n];
|
|
|
|
tail++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2011-05-24 20:47:07 +04:00
|
|
|
int bx_sound_osx_c::openwaveinput(const char *wavedev, sound_record_handler_t rh)
|
|
|
|
{
|
|
|
|
UNUSED(wavedev);
|
|
|
|
record_handler = rh;
|
|
|
|
if (rh != NULL) {
|
|
|
|
record_timer_index = bx_pc_system.register_timer(this, record_timer_handler, 1, 1, 0, "soundosx");
|
|
|
|
// record timer: inactive, continuous, frequency variable
|
|
|
|
}
|
|
|
|
// TODO
|
|
|
|
return BX_SOUNDLOW_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
int bx_sound_osx_c::startwaverecord(int frequency, int bits, bx_bool stereo, int format)
|
|
|
|
{
|
|
|
|
Bit64u timer_val;
|
|
|
|
Bit8u shift = 0;
|
|
|
|
|
|
|
|
UNUSED(format);
|
|
|
|
if (record_timer_index != BX_NULL_TIMER_HANDLE) {
|
|
|
|
if (bits == 16) shift++;
|
|
|
|
if (stereo) shift++;
|
|
|
|
record_packet_size = (frequency / 10) << shift; // 0.1 sec
|
|
|
|
if (record_packet_size > BX_SOUNDLOW_WAVEPACKETSIZE) {
|
|
|
|
record_packet_size = BX_SOUNDLOW_WAVEPACKETSIZE;
|
|
|
|
}
|
|
|
|
timer_val = (Bit64u)record_packet_size * 1000000 / (frequency << shift);
|
|
|
|
bx_pc_system.activate_timer(record_timer_index, (Bit32u)timer_val, 1);
|
|
|
|
}
|
|
|
|
// TODO
|
|
|
|
return BX_SOUNDLOW_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
int bx_sound_osx_c::getwavepacket(int length, Bit8u data[])
|
|
|
|
{
|
|
|
|
// TODO
|
|
|
|
memset(data, 0, length);
|
|
|
|
return BX_SOUNDLOW_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
int bx_sound_osx_c::stopwaverecord()
|
|
|
|
{
|
|
|
|
if (record_timer_index != BX_NULL_TIMER_HANDLE) {
|
|
|
|
bx_pc_system.deactivate_timer(record_timer_index);
|
|
|
|
}
|
|
|
|
// TODO
|
|
|
|
return BX_SOUNDLOW_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
int bx_sound_osx_c::closewaveinput()
|
|
|
|
{
|
|
|
|
stopwaverecord();
|
|
|
|
// TODO
|
|
|
|
return BX_SOUNDLOW_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
void bx_sound_osx_c::record_timer_handler(void *this_ptr)
|
|
|
|
{
|
|
|
|
bx_sound_osx_c *class_ptr = (bx_sound_osx_c *) this_ptr;
|
|
|
|
|
|
|
|
class_ptr->record_timer();
|
|
|
|
}
|
|
|
|
|
|
|
|
void bx_sound_osx_c::record_timer(void)
|
|
|
|
{
|
2013-06-24 23:19:12 +04:00
|
|
|
record_handler(this, record_packet_size);
|
2011-05-24 20:47:07 +04:00
|
|
|
}
|
|
|
|
|
2004-02-10 01:23:53 +03:00
|
|
|
#endif // defined(macintosh)
|