diff --git a/configure b/configure index cf2fc9dee98366305ad6cab308a41aecc4544ee1..bb836082214c2278190e9e174ac40d781be35fe0 100755 --- a/configure +++ b/configure @@ -5781,8 +5781,6 @@ done for ac_header in \ AL/al.h \ AudioToolbox/AudioConverter.h \ - AudioToolbox/AudioFile.h \ - AudioToolbox/AudioFileStream.h \ AudioUnit/AudioUnit.h \ Carbon/Carbon.h \ CoreAudio/CoreAudio.h \ @@ -6547,23 +6545,11 @@ fi COREAUDIO="-framework CoreAudio -framework AudioUnit -framework CoreServices -framework AudioToolbox -framework CoreMIDI" ac_save_LIBS="$LIBS" - if test "$ac_cv_header_AudioToolbox_AudioFile_h" = "yes" + if test "$ac_cv_header_AudioToolbox_AudioConverter_h" = "yes" then LIBS="$LIBS $COREAUDIO" - for ac_func in AudioFileStreamOpen -do : - ac_fn_c_check_func "$LINENO" "AudioFileStreamOpen" "ac_cv_func_AudioFileStreamOpen" -if test "x$ac_cv_func_AudioFileStreamOpen" = x""yes; then : - cat >>confdefs.h <<_ACEOF -#define HAVE_AUDIOFILESTREAMOPEN 1 -_ACEOF - -fi -done - LIBS="$ac_save_LIBS" fi - test "x$ac_cv_func_AudioFileStreamOpen" = xyes || as_fn_append wine_notices "|AudioToolbox version too old, mp3 codec won't be supported." fi if test "$ac_cv_header_OpenAL_al_h" = "yes" then @@ -11478,7 +11464,7 @@ if test "x$ac_cv_lib_mpg123_mpg123_feed" != xyes -a x"$ac_cv_header_CoreAudio_Co This is an error since --with-mpg123 was requested." "$LINENO" 5 ;; esac fi -test "x$ac_cv_lib_mpg123_mpg123_feed" = xyes -o "x$ac_cv_func_AudioFileStreamOpen" = xyes || enable_winemp3_acm=${enable_winemp3_acm:-no} +test "x$ac_cv_lib_mpg123_mpg123_feed" = xyes -o "x$ac_cv_header_AudioToolbox_AudioConverter_h" = xyes || enable_winemp3_acm=${enable_winemp3_acm:-no} if test "$ac_cv_header_AL_al_h" = "yes" then diff --git a/configure.ac b/configure.ac index 8d2dffe2245952b5cb496834ec21103041b921b3..a338d8cfefe6624420a0bab7f629f913995903ef 100644 --- a/configure.ac +++ b/configure.ac @@ -367,8 +367,6 @@ AC_SYS_LARGEFILE() AC_CHECK_HEADERS(\ AL/al.h \ AudioToolbox/AudioConverter.h \ - AudioToolbox/AudioFile.h \ - AudioToolbox/AudioFileStream.h \ AudioUnit/AudioUnit.h \ Carbon/Carbon.h \ CoreAudio/CoreAudio.h \ @@ -707,13 +705,11 @@ case $host_os in dnl CoreServices needed by AudioUnit AC_SUBST(COREAUDIO,"-framework CoreAudio -framework AudioUnit -framework CoreServices -framework AudioToolbox -framework CoreMIDI") ac_save_LIBS="$LIBS" - if test "$ac_cv_header_AudioToolbox_AudioFile_h" = "yes" + if test "$ac_cv_header_AudioToolbox_AudioConverter_h" = "yes" then LIBS="$LIBS $COREAUDIO" - AC_CHECK_FUNCS(AudioFileStreamOpen) LIBS="$ac_save_LIBS" fi - test "x$ac_cv_func_AudioFileStreamOpen" = xyes || WINE_NOTICE([AudioToolbox version too old, mp3 codec won't be supported.]) fi if test "$ac_cv_header_OpenAL_al_h" = "yes" then @@ -1534,7 +1530,7 @@ then fi WINE_NOTICE_WITH(mpg123,[test "x$ac_cv_lib_mpg123_mpg123_feed" != xyes -a x"$ac_cv_header_CoreAudio_CoreAudio_h" != xyes], [libmpg123 ${notice_platform}development files not found (or too old), mp3 codec won't be supported.]) -test "x$ac_cv_lib_mpg123_mpg123_feed" = xyes -o "x$ac_cv_func_AudioFileStreamOpen" = xyes || enable_winemp3_acm=${enable_winemp3_acm:-no} +test "x$ac_cv_lib_mpg123_mpg123_feed" = xyes -o "x$ac_cv_header_AudioToolbox_AudioConverter_h" = xyes || enable_winemp3_acm=${enable_winemp3_acm:-no} dnl **** Check for OpenAL 1.1 **** if test "$ac_cv_header_AL_al_h" = "yes" diff --git a/dlls/winemp3.acm/mpegl3.c b/dlls/winemp3.acm/mpegl3.c index 6a2dbee6c5744299a6c23faae8117dca737d984e..92a3afc2ed34ffb52e9e8a4846ca4c75b725b9cf 100644 --- a/dlls/winemp3.acm/mpegl3.c +++ b/dlls/winemp3.acm/mpegl3.c @@ -37,12 +37,6 @@ # ifdef HAVE_AUDIOTOOLBOX_AUDIOCONVERTER_H # include <AudioToolbox/AudioConverter.h> # endif -# ifdef HAVE_AUDIOTOOLBOX_AUDIOFILE_H -# include <AudioToolbox/AudioFile.h> -# endif -# ifdef HAVE_AUDIOTOOLBOX_AUDIOFILESTREAM_H -# include <AudioToolbox/AudioFileStream.h> -# endif #endif #include "windef.h" @@ -279,32 +273,36 @@ static LRESULT MPEG3_StreamClose(PACMDRVSTREAMINSTANCE adsi) return MMSYSERR_NOERROR; } -#elif defined(HAVE_AUDIOFILESTREAMOPEN) +#elif defined(HAVE_AUDIOTOOLBOX_AUDIOCONVERTER_H) + +static const unsigned short Mp3BitRates[2][16] = +{ + {0, 32, 40, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 256, 320, 0}, + {0, 8, 16, 24, 32, 40, 48, 56, 64, 80, 96, 112, 128, 144, 160, 0} +}; + +static const unsigned short Mp3SampleRates[2][4] = +{ + {44100, 48000, 32000, 0}, + {22050, 24000, 16000, 0} +}; typedef struct tagAcmMpeg3Data { - LRESULT (*convert)(PACMDRVSTREAMINSTANCE adsi, - const unsigned char*, LPDWORD, unsigned char*, LPDWORD); + LRESULT (*convert)(PACMDRVSTREAMINSTANCE adsi, const unsigned char*, + LPDWORD, unsigned char*, LPDWORD); AudioConverterRef acr; AudioStreamBasicDescription in,out; - AudioFileStreamID afs; AudioBufferList outBuffer; AudioBuffer inBuffer; + SInt32 tagBytesLeft; + UInt32 NumberPackets; AudioStreamPacketDescription *PacketDescriptions; - - OSStatus lastError; } AcmMpeg3Data; -static inline const char* wine_dbgstr_fourcc(ULONG fourcc) -{ - char buf[4] = { (char) (fourcc >> 24), (char) (fourcc >> 16), - (char) (fourcc >> 8), (char) fourcc }; - return wine_dbgstr_an(buf, sizeof(buf)); -} - /*********************************************************************** * MPEG3_drvOpen */ @@ -321,8 +319,12 @@ static LRESULT MPEG3_drvClose(DWORD_PTR dwDevID) return 1; } - -static OSStatus Mp3AudioConverterComplexInputDataProc ( +/* + When it asks for data, give it all we have. If we have no data, we assume + we will in the future, so give it no packets and return an error, which + signals that we will have more later. + */ +static OSStatus Mp3AudioConverterComplexInputDataProc( AudioConverterRef inAudioConverter, UInt32 *ioNumberDataPackets, AudioBufferList *ioData, @@ -336,49 +338,218 @@ static OSStatus Mp3AudioConverterComplexInputDataProc ( { *ioNumberDataPackets = amd->NumberPackets; ioData->mNumberBuffers = 1; - ioData->mBuffers[0].mDataByteSize = amd->inBuffer.mDataByteSize; - ioData->mBuffers[0].mData = amd->inBuffer.mData; - ioData->mBuffers[0].mNumberChannels = amd->inBuffer.mNumberChannels; + ioData->mBuffers[0] = amd->inBuffer; amd->inBuffer.mDataByteSize = 0; if (outDataPacketDescription) *outDataPacketDescription = amd->PacketDescriptions; + return noErr; } else + { *ioNumberDataPackets = 0; - return noErr; + return -74; + } +} + +/* + Get the length of the current frame. We need to be at the start of a + frame now. The buffer must have at least the four bytes for the header. + */ +static SInt32 Mp3GetPacketLength(const unsigned char* src) +{ + unsigned char mpegv; + unsigned short brate, srate; + unsigned int size; + + /* + Check that our position looks like an MP3 header and see which type + of MP3 file we have. + */ + if (src[0] == 0xff && src[1] >> 1 == 0x7d) mpegv = 0; /* MPEG-1 File */ + else if (src[0] == 0xff && src[1] >> 1 == 0x79) mpegv = 1; /* MPEG-2 File */ + else return -1; + + /* Fill in bit rate and sample rate. */ + brate = Mp3BitRates[mpegv][(src[2] & 0xf0) >> 4]; + srate = Mp3SampleRates[mpegv][(src[2] & 0xc) >> 2]; + + /* Certain values for bit rate and sample rate are invalid. */ + if (brate == 0 || srate == 0) return -1; + + /* Compute frame size, round down */ + size = 72 * (2 - mpegv) * brate * 1000 / srate; + + /* If there is padding, add one byte */ + if (src[2] & 0x2) return size + 1; + else return size; } +/* + Apple's AudioFileStream does weird things so we deal with parsing the + file ourselves. It was also designed for a different use case, so this + is not unexpected. We expect to have MP3 data as input (i.e. we can only + deal with MPEG-1 or MPEG-2 Layer III), which simplifies parsing a bit. We + understand the ID3v2 header and skip over it. Whenever we have data we + want to skip at the beginning of the input, we do this by setting *ndst=0 + and *nsrc to the length of the unwanted data and return no error. + */ static LRESULT mp3_leopard_horse(PACMDRVSTREAMINSTANCE adsi, - const unsigned char* src, LPDWORD nsrc, - unsigned char* dst, LPDWORD ndst) + const unsigned char* src, LPDWORD nsrc, + unsigned char* dst, LPDWORD ndst) { - AcmMpeg3Data* amd = (AcmMpeg3Data*)adsi->dwDriver; - OSStatus ret; + OSStatus err; + UInt32 size, aspdi, synci, syncSkip; + short framelen[4]; + const unsigned char* psrc; + AcmMpeg3Data* amd = (AcmMpeg3Data*)adsi->dwDriver; + + TRACE("ndst %u %p <- %u %p\n", *ndst, dst, *nsrc, src); + + TRACE("First 16 bytes to input: %s\n", wine_dbgstr_an(src, 16)); + + /* Parse ID3 tag */ + if (!memcmp(src, "ID3", 3) && amd->tagBytesLeft == -1) + { + amd->tagBytesLeft = (src[6] << 21) + (src[7] << 14) + (src[8] << 7) + src[9]; + if (src[5] & 0x10) amd->tagBytesLeft += 20; /* There is a footer */ + else amd->tagBytesLeft += 10; + } + + /* Consume the tag */ + if (amd->tagBytesLeft >= (SInt32)*nsrc) + { + *ndst = 0; + amd->tagBytesLeft -= *nsrc; + + TRACE("All %d bytes of source data is ID3 tag\n", *nsrc); + return MMSYSERR_NOERROR; + } + else if (amd->tagBytesLeft > 0) + { + src += amd->tagBytesLeft; + *nsrc -= amd->tagBytesLeft; + TRACE("Skipping %d for ID3 tag\n", amd->tagBytesLeft); + } + + /* + Sync to initial MP3 frame. The largest possible MP3 frame is 1440. + Thus, in the first 1440 bytes we must find the beginning of 3 valid + frames in a row unless we reach the end of the file first. + */ + syncSkip = 0; + for (psrc = src; psrc <= src + *nsrc - 4 && psrc < src + 1440; psrc++) + { + framelen[0] = 0; + for (synci = 1; + synci < 4 && psrc + framelen[synci-1] < src + *nsrc - 4; + synci++) + { + framelen[synci] = Mp3GetPacketLength(psrc + framelen[synci-1]); + if (framelen[synci] == -1) + { + synci = 0; + break; + } + framelen[synci] += framelen[synci-1]; + } + if (synci > 0) /* We synced successfully */ + { + if (psrc - src > 0) + { + syncSkip = psrc - src; + src += syncSkip; + *nsrc -= syncSkip; + TRACE("Skipping %d for frame sync\n", syncSkip); + } + break; + } + } + + if (Mp3GetPacketLength(src) == -1) + { + *ndst = *nsrc = 0; + ERR("Frame sync failed. Cannot play file.\n"); + return MMSYSERR_ERROR; + } + + /* + Fill in frame descriptions for all frames. We use an extra pointer + to keep track of our position in the input. + */ + + amd->NumberPackets = 25; /* This is the initial array capacity */ + amd->PacketDescriptions = HeapAlloc(GetProcessHeap(), 0, amd->NumberPackets * sizeof(AudioStreamPacketDescription)); + if (amd->PacketDescriptions == 0) return MMSYSERR_NOMEM; + + for (aspdi = 0, psrc = src; + psrc <= src + *nsrc - 4; + psrc += amd->PacketDescriptions[aspdi].mDataByteSize, aspdi++) + { + /* Return an error if we can't read the frame header */ + if (Mp3GetPacketLength(psrc) == -1) + { + *ndst = *nsrc = 0; + ERR("Invalid header at %p.\n", psrc); + HeapFree(GetProcessHeap(), 0, amd->PacketDescriptions); + return MMSYSERR_ERROR; + } + + /* If we run out of space, double size and reallocate */ + if (aspdi >= amd->NumberPackets) + { + amd->NumberPackets *= 2; + amd->PacketDescriptions = HeapReAlloc(GetProcessHeap(), 0, amd->PacketDescriptions, amd->NumberPackets * sizeof(AudioStreamPacketDescription)); + if (amd->PacketDescriptions == 0) return MMSYSERR_NOMEM; + } + + /* Fill in packet data */ + amd->PacketDescriptions[aspdi].mStartOffset = psrc - src; + amd->PacketDescriptions[aspdi].mVariableFramesInPacket = 0; + amd->PacketDescriptions[aspdi].mDataByteSize = Mp3GetPacketLength(psrc); + + /* If this brings us past the end, the last one doesn't count */ + if (psrc + amd->PacketDescriptions[aspdi].mDataByteSize > src + *nsrc) break; + } + + /* Fill in correct number of frames */ + amd->NumberPackets = aspdi; + + /* Adjust nsrc to only include full frames */ + *nsrc = psrc - src; + + amd->inBuffer.mDataByteSize = *nsrc; + amd->inBuffer.mData = src; + amd->inBuffer.mNumberChannels = amd->in.mChannelsPerFrame; - TRACE("ndst %u %p <- %u %p\n",*ndst,dst,*nsrc, src); amd->outBuffer.mNumberBuffers = 1; - amd->outBuffer.mBuffers[0].mDataByteSize = *ndst; + amd->outBuffer.mBuffers[0].mDataByteSize = *ndst; amd->outBuffer.mBuffers[0].mData = dst; - amd->outBuffer.mBuffers[0].mNumberChannels = amd->out.mChannelsPerFrame; + amd->outBuffer.mBuffers[0].mNumberChannels = amd->out.mChannelsPerFrame; + + /* Convert the data */ + size = amd->outBuffer.mBuffers[0].mDataByteSize / amd->out.mBytesPerPacket; + err = AudioConverterFillComplexBuffer(amd->acr, Mp3AudioConverterComplexInputDataProc, amd, &size, &amd->outBuffer, 0); - memset(dst,0xff,*ndst); - ret = AudioFileStreamParseBytes( amd->afs, *nsrc, src, 0 ); + HeapFree(GetProcessHeap(), 0, amd->PacketDescriptions); - if (ret != noErr) + /* Add skipped bytes back into *nsrc */ + if (amd->tagBytesLeft > 0) { - *nsrc = 0; - ERR("Feed Error %s\n", wine_dbgstr_fourcc(ret)); - return MMSYSERR_ERROR; + *nsrc += amd->tagBytesLeft; + amd->tagBytesLeft = 0; } - else if (amd->lastError != noErr) + *nsrc += syncSkip; + + if (err != noErr && err != -74) { - *nsrc = 0; - ERR("Error during feed %s\n", wine_dbgstr_fourcc(ret)); + *ndst = *nsrc = 0; + ERR("Feed Error: %ld\n", err); return MMSYSERR_ERROR; } *ndst = amd->outBuffer.mBuffers[0].mDataByteSize; - *nsrc = amd->inBuffer.mDataByteSize; + + TRACE("convert %d -> %d\n", *nsrc, *ndst); return MMSYSERR_NOERROR; } @@ -389,33 +560,7 @@ static LRESULT mp3_leopard_horse(PACMDRVSTREAMINSTANCE adsi, */ static void MPEG3_Reset(PACMDRVSTREAMINSTANCE adsi, AcmMpeg3Data* aad) { - SInt64 offset; AudioConverterReset(aad->acr); - AudioFileStreamSeek(aad->afs, 0, &offset, 0); -} - -static void Mp3PropertyListenerProc ( void *inClientData, - AudioFileStreamID inAudioFileStream, UInt32 inPropertyID, UInt32 *ioFlags) -{ - /* No operation at this time */ -} - -static void Mp3PacketsProc ( void *inClientData, UInt32 inNumberBytes, - UInt32 inNumberPackets, const void *inInputData, - AudioStreamPacketDescription *inPacketDescriptions) -{ - AcmMpeg3Data *amd = (AcmMpeg3Data*)inClientData; - UInt32 size; - - amd->inBuffer.mDataByteSize = inNumberBytes; - amd->inBuffer.mData = (void*)inInputData; - amd->inBuffer.mNumberChannels = amd->in.mChannelsPerFrame; - - amd->NumberPackets = inNumberPackets; - amd->PacketDescriptions = inPacketDescriptions; - - size = amd->outBuffer.mBuffers[0].mDataByteSize / amd->out.mBytesPerPacket; - amd->lastError = AudioConverterFillComplexBuffer(amd->acr, Mp3AudioConverterComplexInputDataProc, inClientData, &size, &amd->outBuffer, NULL); } /*********************************************************************** @@ -437,13 +582,8 @@ static LRESULT MPEG3_StreamOpen(PACMDRVSTREAMINSTANCE adsi) adsi->dwDriver = (DWORD_PTR)aad; - if (adsi->pwfxSrc->wFormatTag == WAVE_FORMAT_PCM && + if (adsi->pwfxSrc->wFormatTag == WAVE_FORMAT_MPEGLAYER3 && adsi->pwfxDst->wFormatTag == WAVE_FORMAT_PCM) - { - goto theEnd; - } - else if (adsi->pwfxSrc->wFormatTag == WAVE_FORMAT_MPEGLAYER3 && - adsi->pwfxDst->wFormatTag == WAVE_FORMAT_PCM) { OSStatus err; @@ -458,7 +598,6 @@ static LRESULT MPEG3_StreamOpen(PACMDRVSTREAMINSTANCE adsi) aad->in.mFormatFlags = 0; aad->out.mFormatFlags = kLinearPCMFormatFlagIsSignedInteger; aad->in.mBytesPerFrame = 0; - aad->out.mBytesPerFrame = (aad->out.mBitsPerChannel * aad->out.mChannelsPerFrame) / 8; aad->in.mBytesPerPacket = 0; aad->out.mBytesPerPacket = aad->out.mBytesPerFrame; @@ -466,33 +605,26 @@ static LRESULT MPEG3_StreamOpen(PACMDRVSTREAMINSTANCE adsi) aad->out.mFramesPerPacket = 1; aad->in.mReserved = aad->out.mReserved = 0; - aad->acr = NULL; + aad->tagBytesLeft = -1; + + aad->convert = mp3_leopard_horse; - err = AudioConverterNew(&aad->in, &aad->out ,&aad->acr); + err = AudioConverterNew(&aad->in, &aad->out, &aad->acr); if (err != noErr) { - ERR("Create failed: %s\n", wine_dbgstr_fourcc(err)); - goto theEnd; + ERR("Create failed: %ld\n", err); } else { - aad->convert = mp3_leopard_horse; - err = AudioFileStreamOpen(aad, Mp3PropertyListenerProc, Mp3PacketsProc, kAudioFormatMPEGLayer3, &aad->afs); - if (err != noErr) - { - ERR("Stream Open failed: %s\n", wine_dbgstr_fourcc(err)); - goto theEnd; - } + MPEG3_Reset(adsi, aad); + + return MMSYSERR_NOERROR; } } - else goto theEnd; - MPEG3_Reset(adsi, aad); - - return MMSYSERR_NOERROR; - theEnd: HeapFree(GetProcessHeap(), 0, aad); - adsi->dwDriver = 0L; + adsi->dwDriver = 0; + return MMSYSERR_NOTSUPPORTED; } @@ -502,9 +634,13 @@ static LRESULT MPEG3_StreamOpen(PACMDRVSTREAMINSTANCE adsi) */ static LRESULT MPEG3_StreamClose(PACMDRVSTREAMINSTANCE adsi) { - AudioConverterDispose(((AcmMpeg3Data*)adsi->dwDriver)->acr); - AudioFileStreamClose(((AcmMpeg3Data*)adsi->dwDriver)->afs); - HeapFree(GetProcessHeap(), 0, (void*)adsi->dwDriver); + AcmMpeg3Data* amd = (AcmMpeg3Data*)adsi->dwDriver; + + AudioConverterDispose(amd->acr); + + HeapFree(GetProcessHeap(), 0, amd); + adsi->dwDriver = 0; + return MMSYSERR_NOERROR; } diff --git a/include/config.h.in b/include/config.h.in index 5d8806ddc97c7587c289cc7e484bba79d83d67f6..f365fca47e9e54e344eda048056cd096a5d09630 100644 --- a/include/config.h.in +++ b/include/config.h.in @@ -33,19 +33,9 @@ /* Define to 1 if you have the <asm/types.h> header file. */ #undef HAVE_ASM_TYPES_H -/* Define to 1 if you have the `AudioFileStreamOpen' function. */ -#undef HAVE_AUDIOFILESTREAMOPEN - /* Define to 1 if you have the <AudioToolbox/AudioConverter.h> header file. */ #undef HAVE_AUDIOTOOLBOX_AUDIOCONVERTER_H -/* Define to 1 if you have the <AudioToolbox/AudioFileStream.h> header file. - */ -#undef HAVE_AUDIOTOOLBOX_AUDIOFILESTREAM_H - -/* Define to 1 if you have the <AudioToolbox/AudioFile.h> header file. */ -#undef HAVE_AUDIOTOOLBOX_AUDIOFILE_H - /* Define to 1 if you have the <AudioUnit/AudioUnit.h> header file. */ #undef HAVE_AUDIOUNIT_AUDIOUNIT_H