SndFile.c revision 4b65ef9efdf5aba01bea89d8cdd64f500560a28d
1b7154f2324c8ae44b820c07c69aaa80a4bb9e418Glenn Kasten/*
2b7154f2324c8ae44b820c07c69aaa80a4bb9e418Glenn Kasten * Copyright (C) 2010 The Android Open Source Project
3b7154f2324c8ae44b820c07c69aaa80a4bb9e418Glenn Kasten *
4b7154f2324c8ae44b820c07c69aaa80a4bb9e418Glenn Kasten * Licensed under the Apache License, Version 2.0 (the "License");
5b7154f2324c8ae44b820c07c69aaa80a4bb9e418Glenn Kasten * you may not use this file except in compliance with the License.
6b7154f2324c8ae44b820c07c69aaa80a4bb9e418Glenn Kasten * You may obtain a copy of the License at
7b7154f2324c8ae44b820c07c69aaa80a4bb9e418Glenn Kasten *
8b7154f2324c8ae44b820c07c69aaa80a4bb9e418Glenn Kasten *      http://www.apache.org/licenses/LICENSE-2.0
9b7154f2324c8ae44b820c07c69aaa80a4bb9e418Glenn Kasten *
10b7154f2324c8ae44b820c07c69aaa80a4bb9e418Glenn Kasten * Unless required by applicable law or agreed to in writing, software
11b7154f2324c8ae44b820c07c69aaa80a4bb9e418Glenn Kasten * distributed under the License is distributed on an "AS IS" BASIS,
12b7154f2324c8ae44b820c07c69aaa80a4bb9e418Glenn Kasten * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13b7154f2324c8ae44b820c07c69aaa80a4bb9e418Glenn Kasten * See the License for the specific language governing permissions and
14b7154f2324c8ae44b820c07c69aaa80a4bb9e418Glenn Kasten * limitations under the License.
15b7154f2324c8ae44b820c07c69aaa80a4bb9e418Glenn Kasten */
16b7154f2324c8ae44b820c07c69aaa80a4bb9e418Glenn Kasten
17b7154f2324c8ae44b820c07c69aaa80a4bb9e418Glenn Kasten/* libsndfile integration */
18b7154f2324c8ae44b820c07c69aaa80a4bb9e418Glenn Kasten
19b7154f2324c8ae44b820c07c69aaa80a4bb9e418Glenn Kasten#include "sles_allinclusive.h"
20b7154f2324c8ae44b820c07c69aaa80a4bb9e418Glenn Kasten
21b7154f2324c8ae44b820c07c69aaa80a4bb9e418Glenn Kasten#ifdef USE_SNDFILE
22b7154f2324c8ae44b820c07c69aaa80a4bb9e418Glenn Kasten
236a357b8fa57b0bc1557cd5ab9f9fb86aabaaa18cGlenn Kastenvoid SndFile_Callback(SLBufferQueueItf caller, void *pContext)
24b7154f2324c8ae44b820c07c69aaa80a4bb9e418Glenn Kasten{
25276cab2d983b892d1b634474b6249f6bec400c76Glenn Kasten    CAudioPlayer *thisAP = (CAudioPlayer *) pContext;
26e5bf0d2c9531a7064eb3ddcafaf93ac1b0974037Glenn Kasten    object_lock_peek(&thisAP->mObject);
27e5bf0d2c9531a7064eb3ddcafaf93ac1b0974037Glenn Kasten    SLuint32 state = thisAP->mPlay.mState;
28e5bf0d2c9531a7064eb3ddcafaf93ac1b0974037Glenn Kasten    object_unlock_peek(&thisAP->mObject);
294b65ef9efdf5aba01bea89d8cdd64f500560a28dGlenn Kasten    // FIXME should not muck around directly at this low level
30e5bf0d2c9531a7064eb3ddcafaf93ac1b0974037Glenn Kasten    if (SL_PLAYSTATE_PLAYING != state)
31276cab2d983b892d1b634474b6249f6bec400c76Glenn Kasten        return;
32276cab2d983b892d1b634474b6249f6bec400c76Glenn Kasten    struct SndFile *this = &thisAP->mSndFile;
33b7154f2324c8ae44b820c07c69aaa80a4bb9e418Glenn Kasten    SLresult result;
344b65ef9efdf5aba01bea89d8cdd64f500560a28dGlenn Kasten    pthread_mutex_lock(&this->mMutex);
354b65ef9efdf5aba01bea89d8cdd64f500560a28dGlenn Kasten    if ((NULL != this->mRetryBuffer) && (0 < this->mRetrySize)) {
3627f8dfaea17ab7831a1bd34a02f85d55bacf67b7Glenn Kasten        result = (*caller)->Enqueue(caller, this->mRetryBuffer, this->mRetrySize);
374b65ef9efdf5aba01bea89d8cdd64f500560a28dGlenn Kasten        if (SL_RESULT_BUFFER_INSUFFICIENT == result) {
384b65ef9efdf5aba01bea89d8cdd64f500560a28dGlenn Kasten            pthread_mutex_unlock(&this->mMutex);
39b7154f2324c8ae44b820c07c69aaa80a4bb9e418Glenn Kasten            return;     // what, again?
404b65ef9efdf5aba01bea89d8cdd64f500560a28dGlenn Kasten        }
41b7154f2324c8ae44b820c07c69aaa80a4bb9e418Glenn Kasten        assert(SL_RESULT_SUCCESS == result);
42b7154f2324c8ae44b820c07c69aaa80a4bb9e418Glenn Kasten        this->mRetryBuffer = NULL;
43b7154f2324c8ae44b820c07c69aaa80a4bb9e418Glenn Kasten        this->mRetrySize = 0;
444b65ef9efdf5aba01bea89d8cdd64f500560a28dGlenn Kasten        pthread_mutex_unlock(&this->mMutex);
45b7154f2324c8ae44b820c07c69aaa80a4bb9e418Glenn Kasten        return;
46b7154f2324c8ae44b820c07c69aaa80a4bb9e418Glenn Kasten    }
47e5bf0d2c9531a7064eb3ddcafaf93ac1b0974037Glenn Kasten    if (this->mEOF) {
48e5bf0d2c9531a7064eb3ddcafaf93ac1b0974037Glenn Kasten        pthread_mutex_unlock(&this->mMutex);
49e5bf0d2c9531a7064eb3ddcafaf93ac1b0974037Glenn Kasten        return;
50e5bf0d2c9531a7064eb3ddcafaf93ac1b0974037Glenn Kasten    }
516a357b8fa57b0bc1557cd5ab9f9fb86aabaaa18cGlenn Kasten    short *pBuffer = &this->mBuffer[this->mWhich * SndFile_BUFSIZE];
526a357b8fa57b0bc1557cd5ab9f9fb86aabaaa18cGlenn Kasten    if (++this->mWhich >= SndFile_NUMBUFS)
536a357b8fa57b0bc1557cd5ab9f9fb86aabaaa18cGlenn Kasten        this->mWhich = 0;
54b7154f2324c8ae44b820c07c69aaa80a4bb9e418Glenn Kasten    sf_count_t count;
556a357b8fa57b0bc1557cd5ab9f9fb86aabaaa18cGlenn Kasten    count = sf_read_short(this->mSNDFILE, pBuffer, (sf_count_t) SndFile_BUFSIZE);
56e5bf0d2c9531a7064eb3ddcafaf93ac1b0974037Glenn Kasten    if (0 >= count)
57e5bf0d2c9531a7064eb3ddcafaf93ac1b0974037Glenn Kasten        this->mEOF = SL_BOOLEAN_TRUE;
58e5bf0d2c9531a7064eb3ddcafaf93ac1b0974037Glenn Kasten    pthread_mutex_unlock(&this->mMutex);
59b7154f2324c8ae44b820c07c69aaa80a4bb9e418Glenn Kasten    if (0 < count) {
607324a5ab12cc734e2feb4cef8baeda26566d3c92Glenn Kasten        SLuint32 size = (SLuint32) (count * sizeof(short));
61b7154f2324c8ae44b820c07c69aaa80a4bb9e418Glenn Kasten        result = (*caller)->Enqueue(caller, pBuffer, size);
624b65ef9efdf5aba01bea89d8cdd64f500560a28dGlenn Kasten        // this should not happen, but if it does, who will call us to kick off again?
63b7154f2324c8ae44b820c07c69aaa80a4bb9e418Glenn Kasten        if (SL_RESULT_BUFFER_INSUFFICIENT == result) {
64b7154f2324c8ae44b820c07c69aaa80a4bb9e418Glenn Kasten            this->mRetryBuffer = pBuffer;
65b7154f2324c8ae44b820c07c69aaa80a4bb9e418Glenn Kasten            this->mRetrySize = size;
66b7154f2324c8ae44b820c07c69aaa80a4bb9e418Glenn Kasten            return;
67b7154f2324c8ae44b820c07c69aaa80a4bb9e418Glenn Kasten        }
68b7154f2324c8ae44b820c07c69aaa80a4bb9e418Glenn Kasten        assert(SL_RESULT_SUCCESS == result);
69276cab2d983b892d1b634474b6249f6bec400c76Glenn Kasten    } else {
70e5bf0d2c9531a7064eb3ddcafaf93ac1b0974037Glenn Kasten        object_lock_exclusive(&thisAP->mObject);
714b65ef9efdf5aba01bea89d8cdd64f500560a28dGlenn Kasten        // FIXME This is really hosed, you can't do this anymore!
724b65ef9efdf5aba01bea89d8cdd64f500560a28dGlenn Kasten        // FIXME Need a state PAUSE_WHEN_EMPTY
737a79f519d89eb0e1a5b3f4005484b16d6854d7e2Glenn Kasten        // Should not pause yet - we just ran out of new data to enqueue,
74e5bf0d2c9531a7064eb3ddcafaf93ac1b0974037Glenn Kasten        // but there may still be (partially) full buffers in the queue.
75276cab2d983b892d1b634474b6249f6bec400c76Glenn Kasten        thisAP->mPlay.mState = SL_PLAYSTATE_PAUSED;
76e5bf0d2c9531a7064eb3ddcafaf93ac1b0974037Glenn Kasten        thisAP->mPlay.mPosition = thisAP->mPlay.mDuration;
77e5bf0d2c9531a7064eb3ddcafaf93ac1b0974037Glenn Kasten        object_unlock_exclusive_attributes(&thisAP->mObject, ATTR_TRANSPORT);
78b7154f2324c8ae44b820c07c69aaa80a4bb9e418Glenn Kasten    }
79b7154f2324c8ae44b820c07c69aaa80a4bb9e418Glenn Kasten}
80b7154f2324c8ae44b820c07c69aaa80a4bb9e418Glenn Kasten
81b7154f2324c8ae44b820c07c69aaa80a4bb9e418Glenn KastenSLboolean SndFile_IsSupported(const SF_INFO *sfinfo)
82b7154f2324c8ae44b820c07c69aaa80a4bb9e418Glenn Kasten{
83b7154f2324c8ae44b820c07c69aaa80a4bb9e418Glenn Kasten    switch (sfinfo->format & SF_FORMAT_TYPEMASK) {
84b7154f2324c8ae44b820c07c69aaa80a4bb9e418Glenn Kasten    case SF_FORMAT_WAV:
85b7154f2324c8ae44b820c07c69aaa80a4bb9e418Glenn Kasten        break;
86b7154f2324c8ae44b820c07c69aaa80a4bb9e418Glenn Kasten    default:
87b7154f2324c8ae44b820c07c69aaa80a4bb9e418Glenn Kasten        return SL_BOOLEAN_FALSE;
88b7154f2324c8ae44b820c07c69aaa80a4bb9e418Glenn Kasten    }
89b7154f2324c8ae44b820c07c69aaa80a4bb9e418Glenn Kasten    switch (sfinfo->format & SF_FORMAT_SUBMASK) {
90276cab2d983b892d1b634474b6249f6bec400c76Glenn Kasten    case SF_FORMAT_PCM_U8:
91b7154f2324c8ae44b820c07c69aaa80a4bb9e418Glenn Kasten    case SF_FORMAT_PCM_16:
92b7154f2324c8ae44b820c07c69aaa80a4bb9e418Glenn Kasten        break;
93b7154f2324c8ae44b820c07c69aaa80a4bb9e418Glenn Kasten    default:
94b7154f2324c8ae44b820c07c69aaa80a4bb9e418Glenn Kasten        return SL_BOOLEAN_FALSE;
95b7154f2324c8ae44b820c07c69aaa80a4bb9e418Glenn Kasten    }
96b7154f2324c8ae44b820c07c69aaa80a4bb9e418Glenn Kasten    switch (sfinfo->samplerate) {
97276cab2d983b892d1b634474b6249f6bec400c76Glenn Kasten    case 11025:
98276cab2d983b892d1b634474b6249f6bec400c76Glenn Kasten    case 22050:
99b7154f2324c8ae44b820c07c69aaa80a4bb9e418Glenn Kasten    case 44100:
100b7154f2324c8ae44b820c07c69aaa80a4bb9e418Glenn Kasten        break;
101b7154f2324c8ae44b820c07c69aaa80a4bb9e418Glenn Kasten    default:
102b7154f2324c8ae44b820c07c69aaa80a4bb9e418Glenn Kasten        return SL_BOOLEAN_FALSE;
103b7154f2324c8ae44b820c07c69aaa80a4bb9e418Glenn Kasten    }
104b7154f2324c8ae44b820c07c69aaa80a4bb9e418Glenn Kasten    switch (sfinfo->channels) {
105276cab2d983b892d1b634474b6249f6bec400c76Glenn Kasten    case 1:
106b7154f2324c8ae44b820c07c69aaa80a4bb9e418Glenn Kasten    case 2:
107b7154f2324c8ae44b820c07c69aaa80a4bb9e418Glenn Kasten        break;
108b7154f2324c8ae44b820c07c69aaa80a4bb9e418Glenn Kasten    default:
109b7154f2324c8ae44b820c07c69aaa80a4bb9e418Glenn Kasten        return SL_BOOLEAN_FALSE;
110b7154f2324c8ae44b820c07c69aaa80a4bb9e418Glenn Kasten    }
111b7154f2324c8ae44b820c07c69aaa80a4bb9e418Glenn Kasten    return SL_BOOLEAN_TRUE;
112b7154f2324c8ae44b820c07c69aaa80a4bb9e418Glenn Kasten}
113b7154f2324c8ae44b820c07c69aaa80a4bb9e418Glenn Kasten
114acd88797a1d3b8225bab888d29036e245f275be5Glenn KastenSLresult SndFile_checkAudioPlayerSourceSink(CAudioPlayer *this)
115daf661248ff6ea2e21799e5114c78b7c1d49630eGlenn Kasten{
116acd88797a1d3b8225bab888d29036e245f275be5Glenn Kasten    const SLDataSource *pAudioSrc = &this->mDataSource.u.mSource;
117daf661248ff6ea2e21799e5114c78b7c1d49630eGlenn Kasten    SLuint32 locatorType = *(SLuint32 *)pAudioSrc->pLocator;
118daf661248ff6ea2e21799e5114c78b7c1d49630eGlenn Kasten    SLuint32 formatType = *(SLuint32 *)pAudioSrc->pFormat;
119daf661248ff6ea2e21799e5114c78b7c1d49630eGlenn Kasten    switch (locatorType) {
120daf661248ff6ea2e21799e5114c78b7c1d49630eGlenn Kasten    case SL_DATALOCATOR_BUFFERQUEUE:
121daf661248ff6ea2e21799e5114c78b7c1d49630eGlenn Kasten        break;
122daf661248ff6ea2e21799e5114c78b7c1d49630eGlenn Kasten    case SL_DATALOCATOR_URI:
123daf661248ff6ea2e21799e5114c78b7c1d49630eGlenn Kasten        {
124daf661248ff6ea2e21799e5114c78b7c1d49630eGlenn Kasten        SLDataLocator_URI *dl_uri = (SLDataLocator_URI *) pAudioSrc->pLocator;
125daf661248ff6ea2e21799e5114c78b7c1d49630eGlenn Kasten        SLchar *uri = dl_uri->URI;
126daf661248ff6ea2e21799e5114c78b7c1d49630eGlenn Kasten        if (NULL == uri)
127daf661248ff6ea2e21799e5114c78b7c1d49630eGlenn Kasten            return SL_RESULT_PARAMETER_INVALID;
12827f8dfaea17ab7831a1bd34a02f85d55bacf67b7Glenn Kasten        if (!strncmp((const char *) uri, "file:///", 8))
12927f8dfaea17ab7831a1bd34a02f85d55bacf67b7Glenn Kasten            uri += 8;
130daf661248ff6ea2e21799e5114c78b7c1d49630eGlenn Kasten        switch (formatType) {
131a438eb1cf1ae602afab00336528dd230bd929206Glenn Kasten        case SL_DATAFORMAT_NULL:    // OK to omit the data format
132a438eb1cf1ae602afab00336528dd230bd929206Glenn Kasten        case SL_DATAFORMAT_MIME:    // we ignore a MIME type if specified
133daf661248ff6ea2e21799e5114c78b7c1d49630eGlenn Kasten            break;
134daf661248ff6ea2e21799e5114c78b7c1d49630eGlenn Kasten        default:
135daf661248ff6ea2e21799e5114c78b7c1d49630eGlenn Kasten            return SL_RESULT_CONTENT_UNSUPPORTED;
136daf661248ff6ea2e21799e5114c78b7c1d49630eGlenn Kasten        }
13727f8dfaea17ab7831a1bd34a02f85d55bacf67b7Glenn Kasten        this->mSndFile.mPathname = uri;
13840d1c40832a448e23d0bb37512aee53222575c2eGlenn Kasten        this->mBufferQueue.mNumBuffers = SndFile_NUMBUFS;
139daf661248ff6ea2e21799e5114c78b7c1d49630eGlenn Kasten        }
140daf661248ff6ea2e21799e5114c78b7c1d49630eGlenn Kasten        break;
141daf661248ff6ea2e21799e5114c78b7c1d49630eGlenn Kasten    default:
142daf661248ff6ea2e21799e5114c78b7c1d49630eGlenn Kasten        return SL_RESULT_CONTENT_UNSUPPORTED;
143daf661248ff6ea2e21799e5114c78b7c1d49630eGlenn Kasten    }
1446a357b8fa57b0bc1557cd5ab9f9fb86aabaaa18cGlenn Kasten    this->mSndFile.mWhich = 0;
145acd88797a1d3b8225bab888d29036e245f275be5Glenn Kasten    this->mSndFile.mSNDFILE = NULL;
146e5bf0d2c9531a7064eb3ddcafaf93ac1b0974037Glenn Kasten    // this->mSndFile.mMutex is initialized only when there is a valid mSNDFILE
147e5bf0d2c9531a7064eb3ddcafaf93ac1b0974037Glenn Kasten    this->mSndFile.mEOF = SL_BOOLEAN_FALSE;
148acd88797a1d3b8225bab888d29036e245f275be5Glenn Kasten    this->mSndFile.mRetryBuffer = NULL;
149acd88797a1d3b8225bab888d29036e245f275be5Glenn Kasten    this->mSndFile.mRetrySize = 0;
150daf661248ff6ea2e21799e5114c78b7c1d49630eGlenn Kasten    return SL_RESULT_SUCCESS;
151daf661248ff6ea2e21799e5114c78b7c1d49630eGlenn Kasten}
152daf661248ff6ea2e21799e5114c78b7c1d49630eGlenn Kasten
1534b65ef9efdf5aba01bea89d8cdd64f500560a28dGlenn Kasten// called with mutex unlocked for marker and position updates, and play state change
1544b65ef9efdf5aba01bea89d8cdd64f500560a28dGlenn Kasten// FIXME should use two separate hooks since we have separate attributes TRANSPORT and POSITION
1554b65ef9efdf5aba01bea89d8cdd64f500560a28dGlenn Kasten
156e5bf0d2c9531a7064eb3ddcafaf93ac1b0974037Glenn Kastenvoid audioPlayerTransportUpdate(CAudioPlayer *audioPlayer)
157e5bf0d2c9531a7064eb3ddcafaf93ac1b0974037Glenn Kasten{
1584b65ef9efdf5aba01bea89d8cdd64f500560a28dGlenn Kasten
1594b65ef9efdf5aba01bea89d8cdd64f500560a28dGlenn Kasten    if (NULL != audioPlayer->mSndFile.mSNDFILE) {
1604b65ef9efdf5aba01bea89d8cdd64f500560a28dGlenn Kasten
1614b65ef9efdf5aba01bea89d8cdd64f500560a28dGlenn Kasten        object_lock_exclusive(&audioPlayer->mObject);
1624b65ef9efdf5aba01bea89d8cdd64f500560a28dGlenn Kasten        SLmillisecond pos = audioPlayer->mSeek.mPos;
1634b65ef9efdf5aba01bea89d8cdd64f500560a28dGlenn Kasten        audioPlayer->mSeek.mPos = SL_TIME_UNKNOWN;
1644b65ef9efdf5aba01bea89d8cdd64f500560a28dGlenn Kasten        SLboolean empty = 0 == audioPlayer->mBufferQueue.mState.count;
1654b65ef9efdf5aba01bea89d8cdd64f500560a28dGlenn Kasten        object_unlock_exclusive(&audioPlayer->mObject);
1664b65ef9efdf5aba01bea89d8cdd64f500560a28dGlenn Kasten
1674b65ef9efdf5aba01bea89d8cdd64f500560a28dGlenn Kasten        if (SL_TIME_UNKNOWN != pos) {
1684b65ef9efdf5aba01bea89d8cdd64f500560a28dGlenn Kasten
1694b65ef9efdf5aba01bea89d8cdd64f500560a28dGlenn Kasten            // discard any enqueued buffers for the old position
1704b65ef9efdf5aba01bea89d8cdd64f500560a28dGlenn Kasten            (*&audioPlayer->mBufferQueue.mItf)->Clear(&audioPlayer->mBufferQueue.mItf);
1714b65ef9efdf5aba01bea89d8cdd64f500560a28dGlenn Kasten            empty = SL_BOOLEAN_TRUE;
1724b65ef9efdf5aba01bea89d8cdd64f500560a28dGlenn Kasten
1734b65ef9efdf5aba01bea89d8cdd64f500560a28dGlenn Kasten            pthread_mutex_lock(&audioPlayer->mSndFile.mMutex);
1744b65ef9efdf5aba01bea89d8cdd64f500560a28dGlenn Kasten            (void) sf_seek(audioPlayer->mSndFile.mSNDFILE, (sf_count_t) (((long long) pos *
1754b65ef9efdf5aba01bea89d8cdd64f500560a28dGlenn Kasten                audioPlayer->mSndFile.mSfInfo.samplerate) / 1000LL), SEEK_SET);
1764b65ef9efdf5aba01bea89d8cdd64f500560a28dGlenn Kasten            audioPlayer->mSndFile.mEOF = SL_BOOLEAN_FALSE;
1774b65ef9efdf5aba01bea89d8cdd64f500560a28dGlenn Kasten            audioPlayer->mSndFile.mRetryBuffer = NULL;
1784b65ef9efdf5aba01bea89d8cdd64f500560a28dGlenn Kasten            audioPlayer->mSndFile.mRetrySize = 0;
1794b65ef9efdf5aba01bea89d8cdd64f500560a28dGlenn Kasten            audioPlayer->mSndFile.mWhich = 0;
1804b65ef9efdf5aba01bea89d8cdd64f500560a28dGlenn Kasten            pthread_mutex_unlock(&audioPlayer->mSndFile.mMutex);
1814b65ef9efdf5aba01bea89d8cdd64f500560a28dGlenn Kasten
1824b65ef9efdf5aba01bea89d8cdd64f500560a28dGlenn Kasten        }
1834b65ef9efdf5aba01bea89d8cdd64f500560a28dGlenn Kasten
1844b65ef9efdf5aba01bea89d8cdd64f500560a28dGlenn Kasten        // FIXME only on seek or play state change (STOPPED, PAUSED) -> PLAYING
1854b65ef9efdf5aba01bea89d8cdd64f500560a28dGlenn Kasten        if (empty) {
1864b65ef9efdf5aba01bea89d8cdd64f500560a28dGlenn Kasten            SndFile_Callback(&audioPlayer->mBufferQueue.mItf, audioPlayer);
1874b65ef9efdf5aba01bea89d8cdd64f500560a28dGlenn Kasten        }
1884b65ef9efdf5aba01bea89d8cdd64f500560a28dGlenn Kasten
189e5bf0d2c9531a7064eb3ddcafaf93ac1b0974037Glenn Kasten    }
190e5bf0d2c9531a7064eb3ddcafaf93ac1b0974037Glenn Kasten
191e5bf0d2c9531a7064eb3ddcafaf93ac1b0974037Glenn Kasten}
192e5bf0d2c9531a7064eb3ddcafaf93ac1b0974037Glenn Kasten
193b7154f2324c8ae44b820c07c69aaa80a4bb9e418Glenn Kasten#endif // USE_SNDFILE
194