alvinalexander.com | career | drupal | java | mac | mysql | perl | scala | uml | unix  

Java example source code file (PLATFORM_API_MacOSX_PCM.cpp)

This example Java source code file (PLATFORM_API_MacOSX_PCM.cpp) is included in the alvinalexander.com "Java Source Code Warehouse" project. The intent of this project is to help you "Learn Java by Example" TM.

Learn more about this Java project at its project page.

Java - Java tags/keywords

daudio_open, inputcallback, int64, null, os_error0, osstatus, osx_directaudiodevice, osx_directaudiodevice\*, pos2offset, trace0, trace1, trace2, true, uint32

The PLATFORM_API_MacOSX_PCM.cpp Java example source code

/*
 * Copyright (c) 2002, 2012, Oracle and/or its affiliates. All rights reserved.
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 *
 * This code is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License version 2 only, as
 * published by the Free Software Foundation.  Oracle designates this
 * particular file as subject to the "Classpath" exception as provided
 * by Oracle in the LICENSE file that accompanied this code.
 *
 * This code 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 General Public License
 * version 2 for more details (a copy is included in the LICENSE file that
 * accompanied this code).
 *
 * You should have received a copy of the GNU General Public License version
 * 2 along with this work; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 *
 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
 * or visit www.oracle.com if you need additional information or have any
 * questions.
 */

//#define USE_ERROR
//#define USE_TRACE
//#define USE_VERBOSE_TRACE

#include <AudioUnit/AudioUnit.h>
#include <CoreServices/CoreServices.h>
#include <AudioToolbox/AudioConverter.h>
#include <pthread.h>
#include <math.h>
/*
#if !defined(__COREAUDIO_USE_FLAT_INCLUDES__)
#include <CoreAudio/CoreAudioTypes.h>
#else
#include <CoreAudioTypes.h>
#endif
*/

#include "PLATFORM_API_MacOSX_Utils.h"

extern "C" {
#include "Utilities.h"
#include "DirectAudio.h"
}

#if USE_DAUDIO == TRUE


#ifdef USE_TRACE
static void PrintStreamDesc(const AudioStreamBasicDescription *inDesc) {
    TRACE4("ID='%c%c%c%c'", (char)(inDesc->mFormatID >> 24), (char)(inDesc->mFormatID >> 16), (char)(inDesc->mFormatID >> 8), (char)(inDesc->mFormatID));
    TRACE2(", %f Hz, flags=0x%lX", (float)inDesc->mSampleRate, (long unsigned)inDesc->mFormatFlags);
    TRACE2(", %ld channels, %ld bits", (long)inDesc->mChannelsPerFrame, (long)inDesc->mBitsPerChannel);
    TRACE1(", %ld bytes per frame\n", (long)inDesc->mBytesPerFrame);
}
#else
static inline void PrintStreamDesc(const AudioStreamBasicDescription *inDesc) { }
#endif


#define MAX(x, y)   ((x) >= (y) ? (x) : (y))
#define MIN(x, y)   ((x) <= (y) ? (x) : (y))


// =======================================
// MixerProvider functions implementation

static DeviceList deviceCache;

INT32 DAUDIO_GetDirectAudioDeviceCount() {
    deviceCache.Refresh();
    int count = deviceCache.GetCount();
    if (count > 0) {
        // add "default" device
        count++;
        TRACE1("DAUDIO_GetDirectAudioDeviceCount: returns %d devices\n", count);
    } else {
        TRACE0("DAUDIO_GetDirectAudioDeviceCount: no devices found\n");
    }
    return count;
}

INT32 DAUDIO_GetDirectAudioDeviceDescription(INT32 mixerIndex, DirectAudioDeviceDescription *desc) {
    bool result = true;
    desc->deviceID = 0;
    if (mixerIndex == 0) {
        // default device
        strncpy(desc->name, "Default Audio Device", DAUDIO_STRING_LENGTH);
        strncpy(desc->description, "Default Audio Device", DAUDIO_STRING_LENGTH);
        desc->maxSimulLines = -1;
    } else {
        AudioDeviceID deviceID;
        result = deviceCache.GetDeviceInfo(mixerIndex-1, &deviceID, DAUDIO_STRING_LENGTH,
            desc->name, desc->vendor, desc->description, desc->version);
        if (result) {
            desc->deviceID = (INT32)deviceID;
            desc->maxSimulLines = -1;
        }
    }
    return result ? TRUE : FALSE;
}


void DAUDIO_GetFormats(INT32 mixerIndex, INT32 deviceID, int isSource, void* creator) {
    TRACE3(">>DAUDIO_GetFormats mixerIndex=%d deviceID=0x%x isSource=%d\n", (int)mixerIndex, (int)deviceID, isSource);

    AudioDeviceID audioDeviceID = deviceID == 0 ? GetDefaultDevice(isSource) : (AudioDeviceID)deviceID;

    if (audioDeviceID == 0) {
        return;
    }

    int totalChannels = GetChannelCount(audioDeviceID, isSource);

    if (totalChannels == 0) {
        TRACE0("<Resampler::Init\n");
        TRACE0("  inFormat: ");
        PrintStreamDesc(inFormat);
        TRACE0("  outFormat: ");
        PrintStreamDesc(outFormat);
        TRACE1("  inputBufferSize: %d bytes\n", inputBufferSizeInBytes);
        OSStatus err;

        if ((outFormat->mFormatFlags & kAudioFormatFlagIsNonInterleaved) != 0 && outFormat->mChannelsPerFrame != 1) {
            ERROR0("Resampler::Init ERROR: outFormat is non-interleaved\n");
            return false;
        }
        if ((inFormat->mFormatFlags & kAudioFormatFlagIsNonInterleaved) != 0 && inFormat->mChannelsPerFrame != 1) {
            ERROR0("Resampler::Init ERROR: inFormat is non-interleaved\n");
            return false;
        }

        memcpy(&asbdIn, inFormat, sizeof(AudioStreamBasicDescription));
        memcpy(&asbdOut, outFormat, sizeof(AudioStreamBasicDescription));

        err = AudioConverterNew(inFormat, outFormat, &converter);

        if (err || converter == NULL) {
            OS_ERROR1(err, "Resampler::Init (AudioConverterNew), converter=%p", converter);
            return false;
        }

        // allocate buffer for output data
        int maximumInFrames = inputBufferSizeInBytes / inFormat->mBytesPerFrame;
        // take into account trailingFrames
        AudioConverterPrimeInfo primeInfo = {0, 0};
        UInt32 sizePrime = sizeof(primeInfo);
        err = AudioConverterGetProperty(converter, kAudioConverterPrimeInfo, &sizePrime, &primeInfo);
        if (err) {
            OS_ERROR0(err, "Resampler::Init (get kAudioConverterPrimeInfo)");
            // ignore the error
        } else {
            // the default primeMethod is kConverterPrimeMethod_Normal, so we need only trailingFrames
            maximumInFrames += primeInfo.trailingFrames;
        }
        float outBufferSizeInFrames = (outFormat->mSampleRate / inFormat->mSampleRate) * ((float)maximumInFrames);
        // to avoid complex calculation just set outBufferSize as double of the calculated value
        outBufferSize = (int)outBufferSizeInFrames * outFormat->mBytesPerFrame * 2;
        // safety check - consider 256 frame as the minimum input buffer
        int minOutSize = 256 * outFormat->mBytesPerFrame;
        if (outBufferSize < minOutSize) {
            outBufferSize = minOutSize;
        }

        outBuffer = malloc(outBufferSize);

        if (outBuffer == NULL) {
            ERROR1("Resampler::Init ERROR: malloc failed (%d bytes)\n", outBufferSize);
            AudioConverterDispose(converter);
            converter = NULL;
            return false;
        }

        TRACE1("  allocated: %d bytes for output buffer\n", outBufferSize);

        TRACE0("<Resampler::Process: %d bytes, converter = %p\n", len, converter);
        if (converter == NULL) {    // sanity check
            bytesWritten = ringBuffer->Write(srcBuffer, len, false);
        } else {
            InputProcData data;
            data.pThis = this;
            data.data = (Byte *)srcBuffer;
            data.dataSize = len;

            OSStatus err;
            do {
                AudioBufferList abl;    // by default it contains 1 AudioBuffer
                abl.mNumberBuffers = 1;
                abl.mBuffers[0].mNumberChannels = asbdOut.mChannelsPerFrame;
                abl.mBuffers[0].mDataByteSize   = outBufferSize;
                abl.mBuffers[0].mData           = outBuffer;

                UInt32 packets = (UInt32)outBufferSize / asbdOut.mBytesPerPacket;

                TRACE2(">>AudioConverterFillComplexBuffer: request %d packets, provide %d bytes buffer\n",
                    (int)packets, (int)abl.mBuffers[0].mDataByteSize);

                err = AudioConverterFillComplexBuffer(converter, ConverterInputProc, &data, &packets, &abl, NULL);

                TRACE2("<Resampler::Discontinue\n");
        if (converter != NULL) {
            AudioConverterReset(converter);
        }
        TRACE0("<ConverterInputProc: requested %d packets, data contains %d bytes (%d packets)\n",
            (int)*ioNumberDataPackets, (int)data->dataSize, (int)(data->dataSize / data->pThis->asbdIn.mBytesPerPacket));
        if (data->dataSize == 0) {
            // already called & provided all input data
            // interrupt conversion by returning error
            *ioNumberDataPackets = 0;
            TRACE0("  <pThis->asbdIn.mChannelsPerFrame;
        ioData->mBuffers[0].mDataByteSize   = data->dataSize;
        ioData->mBuffers[0].mData           = data->data;

        *ioNumberDataPackets = data->dataSize / data->pThis->asbdIn.mBytesPerPacket;

        // all data has been provided to the converter
        data->dataSize = 0;

        TRACE1("  <OutputCallback: busNum=%d, requested %d frames (%d bytes)\n",
        (int)inBusNumber, (int)inNumberFrames, (int)(inNumberFrames * device->asbd.mBytesPerFrame));
    TRACE3("  abl: %d buffers, buffer[0].channels=%d, buffer.size=%d\n",
        nchannels, (int)audioBuffer->mNumberChannels, (int)audioBuffer->mDataByteSize);

    int bytesToRead = inNumberFrames * device->asbd.mBytesPerFrame;
    if (bytesToRead > (int)audioBuffer->mDataByteSize) {
        TRACE0("--OutputCallback: !!! audioBuffer IS TOO SMALL!!!\n");
        bytesToRead = audioBuffer->mDataByteSize / device->asbd.mBytesPerFrame * device->asbd.mBytesPerFrame;
    }
    int bytesRead = device->ringBuffer.Read(audioBuffer->mData, bytesToRead);
    if (bytesRead < bytesToRead) {
        // no enough data (underrun)
        TRACE2("--OutputCallback: !!! UNDERRUN (read %d bytes of %d)!!!\n", bytesRead, bytesToRead);
        // silence the rest
        memset((Byte*)audioBuffer->mData + bytesRead, 0, bytesToRead-bytesRead);
        bytesRead = bytesToRead;
    }

    audioBuffer->mDataByteSize = (UInt32)bytesRead;
    // SAFETY: set mDataByteSize for all other AudioBuffer in the AudioBufferList to zero
    while (--nchannels > 0) {
        audioBuffer++;
        audioBuffer->mDataByteSize = 0;
    }
    TRACE1("<InputCallback: busNum=%d, timeStamp=%lld, %d frames (%d bytes)\n",
        (int)inBusNumber, (long long)inTimeStamp->mSampleTime, (int)inNumberFrames, (int)(inNumberFrames * device->asbd.mBytesPerFrame));

    AudioBufferList abl;    // by default it contains 1 AudioBuffer
    abl.mNumberBuffers = 1;
    abl.mBuffers[0].mNumberChannels = device->asbd.mChannelsPerFrame;
    abl.mBuffers[0].mDataByteSize   = device->inputBufferSizeInBytes;   // assume this is == (inNumberFrames * device->asbd.mBytesPerFrame)
    abl.mBuffers[0].mData           = NULL;     // request for the audioUnit's buffer

    OSStatus err = AudioUnitRender(device->audioUnit, ioActionFlags, inTimeStamp, inBusNumber, inNumberFrames, &abl);
    if (err) {
        OS_ERROR0(err, "<Discontinue();
            } else {
                TRACE2("  InputCallback (RESAMPLED), continuous: lastWrittenSampleTime = %f, sampleTime=%f\n",
                    (float)device->lastWrittenSampleTime, (float)sampleTime);
            }
            device->lastWrittenSampleTime = sampleTime + inNumberFrames;

            int bytesWritten = device->resampler->Process(abl.mBuffers[0].mData, (int)abl.mBuffers[0].mDataByteSize, &device->ringBuffer);
            TRACE2("<DAUDIO_Open: mixerIndex=%d deviceID=0x%x isSource=%d\n", (int)mixerIndex, (unsigned int)deviceID, isSource);
    TRACE3("  sampleRate=%d sampleSizeInBits=%d channels=%d\n", (int)sampleRate, sampleSizeInBits, channels);
#ifdef USE_TRACE
    {
        AudioDeviceID audioDeviceID = deviceID;
        if (audioDeviceID == 0) {
            // default device
            audioDeviceID = GetDefaultDevice(isSource);
        }
        char name[256];
        OSStatus err = GetAudioObjectProperty(audioDeviceID, kAudioUnitScope_Global, kAudioDevicePropertyDeviceName, 256, &name, 0);
        if (err != noErr) {
            OS_ERROR1(err, "  audioDeviceID=0x%x, name is N/A:", (int)audioDeviceID);
        } else {
            TRACE2("  audioDeviceID=0x%x, name=%s\n", (int)audioDeviceID, name);
        }
    }
#endif

    if (encoding != DAUDIO_PCM) {
        ERROR1("<asbd, sizeof(device->asbd));
    if (err) {
        OS_ERROR0(err, "<asbd.mBytesPerFrame;  // convert frames to bytes
        extraBufferBytes = (int)device->inputBufferSizeInBytes;
    }

    if (device->resampler != NULL) {
        // resampler output format is a user requested format (== ringBuffer format)
        AudioStreamBasicDescription asbdOut; // ringBuffer format
        FillASBDForNonInterleavedPCM(asbdOut, sampleRate, channels, sampleSizeInBits, false, isSigned, isBigEndian);

        // set resampler input buffer size to the HAL buffer size
        if (!device->resampler->Init(&device->asbd, &asbdOut, (int)device->inputBufferSizeInBytes)) {
            ERROR0("<GetOutBufferSize();
    }

    if (!device->ringBuffer.Allocate(bufferSizeInBytes, extraBufferBytes)) {
        ERROR0("<DAUDIO_Write: %d bytes to write\n", byteSize);

    int result = device->ringBuffer.Write(data, byteSize, true);

    TRACE1("<DAUDIO_Read: %d bytes to read\n", byteSize);

    int result = device->ringBuffer.Read(data, byteSize);

    TRACE1("< 0 ? TRUE : FALSE;

    TRACE1("DAUDIO_StillDraining returns %d\n", draining);
    return draining;
}

int DAUDIO_Flush(void* id, int isSource) {
    OSX_DirectAudioDevice *device = (OSX_DirectAudioDevice*)id;
    TRACE0("DAUDIO_Flush\n");

    device->ringBuffer.Flush();

    return TRUE;
}

int DAUDIO_GetAvailable(void* id, int isSource) {
    OSX_DirectAudioDevice *device = (OSX_DirectAudioDevice*)id;

    int bytesInBuffer = device->ringBuffer.GetValidByteCount();
    if (isSource) {
        return device->ringBuffer.GetBufferSize() - bytesInBuffer;
    } else {
        return bytesInBuffer;
    }
}

INT64 DAUDIO_GetBytePosition(void* id, int isSource, INT64 javaBytePos) {
    OSX_DirectAudioDevice *device = (OSX_DirectAudioDevice*)id;
    INT64 position;

    if (isSource) {
        position = javaBytePos - device->ringBuffer.GetValidByteCount();
    } else {
        position = javaBytePos + device->ringBuffer.GetValidByteCount();
    }

    TRACE2("DAUDIO_GetBytePosition returns %lld (javaBytePos = %lld)\n", (long long)position, (long long)javaBytePos);
    return position;
}

void DAUDIO_SetBytePosition(void* id, int isSource, INT64 javaBytePos) {
    // no need javaBytePos (it's available in DAUDIO_GetBytePosition)
}

int DAUDIO_RequiresServicing(void* id, int isSource) {
    return FALSE;
}

void DAUDIO_Service(void* id, int isSource) {
    // unreachable
}

#endif  // USE_DAUDIO == TRUE

Other Java examples (source code examples)

Here is a short list of links related to this Java PLATFORM_API_MacOSX_PCM.cpp source code file:

... this post is sponsored by my books ...

#1 New Release!

FP Best Seller

 

new blog posts

 

Copyright 1998-2021 Alvin Alexander, alvinalexander.com
All Rights Reserved.

A percentage of advertising revenue from
pages under the /java/jwarehouse URI on this website is
paid back to open source projects.