///////////////////////////////////////////////////////////////////////// // $Id: soundosx.cc,v 1.15 2011-02-10 22:58:22 vruppert Exp $ ///////////////////////////////////////////////////////////////////////// // // Copyright (C) 2004-2011 The Bochs Project // // 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 // This file (SOUNDOSX.CC) written and donated by Brian Huffman #ifdef PARANOID #include #endif #include "bochs.h" #if defined(macintosh) && BX_SUPPORT_SB16 #define LOG_THIS device-> #include "soundmod.h" #include "soundosx.h" #if BX_WITH_MACOS #include #else #include #include #include #include #include #include #endif #include #ifdef BX_SOUND_OSX_use_converter OSStatus MyRenderer (void *inRefCon, AudioUnitRenderActionFlags inActionFlags, const AudioTimeStamp *inTimeStamp, UInt32 inBusNumber, AudioBuffer *ioData); OSStatus MyACInputProc (AudioConverterRef inAudioConverter, UInt32* outDataSize, void** outData, void* inUserData); #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 bx_sound_osx_c::bx_sound_osx_c(logfunctions *dev) :bx_sound_output_c(dev) { MidiOpen = 0; WaveOpen = 0; head = 0; tail = 0; for (int i=0; i=1) ? data[0] : 0; Bit8u arg2 = (length >=2) ? data[1] : 0; MusicDeviceMIDIEvent (synthUnit, command, arg1, arg2, delta); } else { MusicDeviceSysEx (synthUnit, data, length); } #endif return BX_SOUND_OUTPUT_OK; } int bx_sound_osx_c::closemidioutput() { BX_DEBUG(("closemidioutput()")); MidiOpen = 0; #ifdef BX_SOUND_OSX_use_converter AUGraphStop (MidiGraph); AUGraphClose (MidiGraph); #endif return BX_SOUND_OUTPUT_OK; } #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 int bx_sound_osx_c::openwaveoutput(char *wavedev) { OSStatus err; BX_DEBUG(("openwaveoutput(%s)", wavedev)); // open the default output unit #ifdef BX_SOUND_OSX_use_quicktime err = SndNewChannel (&WaveChannel, sampledSynth, 0, NewSndCallBackUPP(WaveCallbackProc)); if (err != noErr) return BX_SOUND_OUTPUT_ERR; #endif #ifdef BX_SOUND_OSX_use_converter err = OpenDefaultAudioOutput (&WaveOutputUnit); if (err != noErr) return BX_SOUND_OUTPUT_ERR; AudioUnitInitialize (WaveOutputUnit); // Set up a callback function to generate output to the output unit AudioUnitInputCallback input; input.inputProc = MyRenderer; input.inputProcRefCon = (void *) this; AudioUnitSetProperty (WaveOutputUnit, kAudioUnitProperty_SetInputCallback, kAudioUnitScope_Global, 0, &input, sizeof(input)); #endif WaveOpen = 1; return BX_SOUND_OUTPUT_OK; } int bx_sound_osx_c::startwaveplayback(int frequency, int bits, int stereo, int format) { #ifdef BX_SOUND_OSX_use_converter static int oldfreq, oldbits, oldstereo, oldformat; AudioStreamBasicDescription srcFormat, dstFormat; UInt32 formatSize = sizeof(AudioStreamBasicDescription); #endif BX_DEBUG(("startwaveplayback(%d, %d, %d, %x)", frequency, bits, stereo, format)); #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 if ((frequency == oldfreq) && (bits == oldbits) && (stereo == oldstereo) && (format == oldformat)) return BX_SOUND_OUTPUT_OK; // nothing to do 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; if (WavePlaying) AudioOutputUnitStop (WaveOutputUnit); if (WaveConverter) AudioConverterDispose (WaveConverter); AudioUnitGetProperty (WaveOutputUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Output, 0, &dstFormat, &formatSize); AudioConverterNew (&srcFormat, &dstFormat, &WaveConverter); if (srcFormat.mChannelsPerFrame == 1 && dstFormat.mChannelsPerFrame == 2) { // map single-channel input to both output channels SInt32 map[2] = {0,0}; AudioConverterSetProperty (WaveConverter, kAudioConverterChannelMap, sizeof(map), (void*) map); } if (WavePlaying) AudioOutputUnitStart (WaveOutputUnit); #endif return BX_SOUND_OUTPUT_OK; } 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)) { return BX_SOUND_OUTPUT_OK; } else { #ifdef BX_SOUND_OSX_use_converter // If buffer is full, make sure sound is playing if (WaveOutputUnit && !WavePlaying) { AudioOutputUnitStart (WaveOutputUnit); WavePlaying = 1; } #endif return BX_SOUND_OUTPUT_ERR; } } int bx_sound_osx_c::sendwavepacket(int length, Bit8u data[]) { #ifdef BX_SOUND_OSX_use_quicktime SndCommand mySndCommand; #endif BX_DEBUG(("sendwavepacket(%d, %p), head=%u", length, data, head)); // sanity check if ((!WaveOpen) || (head - tail >= BX_SOUND_OSX_NBUF)) return BX_SOUND_OUTPUT_ERR; // 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) { AudioOutputUnitStart (WaveOutputUnit); WavePlaying = 1; } #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 return BX_SOUND_OUTPUT_OK; } int bx_sound_osx_c::stopwaveplayback() { return BX_SOUND_OUTPUT_OK; } 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 return BX_SOUND_OUTPUT_OK; } #ifdef BX_SOUND_OSX_use_converter OSStatus MyRenderer (void *inRefCon, AudioUnitRenderActionFlags inActionFlags, const AudioTimeStamp *inTimeStamp, UInt32 inBusNumber, AudioBuffer *ioData) { UInt32 size = ioData->mDataByteSize; AudioConverterFillBuffer (WaveConverter, MyACInputProc, inRefCon, &size, ioData->mData); return noErr; } 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; } void bx_sound_osx_c::nextbuffer (int *outDataSize, void **outData) { BX_DEBUG(("nextbuffer(), tail=%u", tail)); if (head - tail <= 0) { *outData = NULL; *outDataSize = 0; // We are getting behind, so stop the output for now AudioOutputUnitStop (WaveOutputUnit); WavePlaying = 0; } else { int n = tail % BX_SOUND_OSX_NBUF; *outData = (void *) (WaveData[n]); *outDataSize = WaveLength[n]; tail++; } } #endif #endif // defined(macintosh)