Merged back audiotrack support from felix lang.

This commit is contained in:
Armin Novak 2013-09-19 12:00:49 +02:00
parent 403a9fff62
commit 4748d0e4dd
5 changed files with 883 additions and 0 deletions

View File

@ -0,0 +1,51 @@
# FreeRDP: A Remote Desktop Protocol Implementation
# FreeRDP cmake build script
#
# Copyright 2012 Marc-Andre Moreau <marcandre.moreau@gmail.com>
# Copyright 2013 Armin Novak <armin.novak@gmail.com>
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
define_channel_client_subsystem("rdpsnd" "audiotrack" "")
set(${MODULE_PREFIX}_SRCS
audiotrack.cpp
rdpsnd_audiotrack.c)
include_directories(.)
include_directories(..)
include_directories(${AUDIOTRACK_INCLUDE_DIRS})
add_channel_client_subsystem_library(${MODULE_PREFIX} ${MODULE_NAME} ${CHANNEL_NAME} "" TRUE "")
set_target_properties(${MODULE_NAME} PROPERTIES PREFIX "")
set_complex_link_libraries(VARIABLE ${MODULE_PREFIX}_LIBS
MONOLITHIC ${MONOLITHIC_BUILD}
MODULE freerdp
MODULES freerdp-codec freerdp-utils)
set_complex_link_libraries(VARIABLE ${MODULE_PREFIX}_LIBS
MONOLITHIC ${MONOLITHIC_BUILD}
MODULE winpr
MODULES winpr-utils)
set(${MODULE_PREFIX}_LIBS ${${MODULE_PREFIX}_LIBS} ${AUDIOTRACK_LIBRARIES})
target_link_libraries(${MODULE_NAME} ${${MODULE_PREFIX}_LIBS})
if(NOT STATIC_CHANNELS)
install(TARGETS ${MODULE_NAME} DESTINATION ${FREERDP_ADDIN_PATH})
endif()
set_property(TARGET ${MODULE_NAME} PROPERTY FOLDER "Channels/${CHANNEL_NAME}/Client/AudioTrack")

View File

@ -0,0 +1,88 @@
/*
* Copyright (C) 2007 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef ANDROID_ERRORS_H
#define ANDROID_ERRORS_H
#include <sys/types.h>
#include <errno.h>
namespace android {
// use this type to return error codes
#ifdef HAVE_MS_C_RUNTIME
typedef int status_t;
#else
typedef int32_t status_t;
#endif
/* the MS C runtime lacks a few error codes */
/*
* Error codes.
* All error codes are negative values.
*/
// Win32 #defines NO_ERROR as well. It has the same value, so there's no
// real conflict, though it's a bit awkward.
#ifdef _WIN32
# undef NO_ERROR
#endif
enum {
OK = 0, // Everything's swell.
NO_ERROR = 0, // No errors.
UNKNOWN_ERROR = 0x80000000,
NO_MEMORY = -ENOMEM,
INVALID_OPERATION = -ENOSYS,
BAD_VALUE = -EINVAL,
BAD_TYPE = 0x80000001,
NAME_NOT_FOUND = -ENOENT,
PERMISSION_DENIED = -EPERM,
NO_INIT = -ENODEV,
ALREADY_EXISTS = -EEXIST,
DEAD_OBJECT = -EPIPE,
FAILED_TRANSACTION = 0x80000002,
JPARKS_BROKE_IT = -EPIPE,
#if !defined(HAVE_MS_C_RUNTIME)
BAD_INDEX = -EOVERFLOW,
NOT_ENOUGH_DATA = -ENODATA,
WOULD_BLOCK = -EWOULDBLOCK,
TIMED_OUT = -ETIME,
UNKNOWN_TRANSACTION = -EBADMSG,
#else
BAD_INDEX = -E2BIG,
NOT_ENOUGH_DATA = 0x80000003,
WOULD_BLOCK = 0x80000004,
TIMED_OUT = 0x80000005,
UNKNOWN_TRANSACTION = 0x80000006,
#endif
};
// Restore define; enumeration is in "android" namespace, so the value defined
// there won't work for Win32 code in a different namespace.
#ifdef _WIN32
# define NO_ERROR 0L
#endif
}; // namespace android
// ---------------------------------------------------------------------------
#endif // ANDROID_ERRORS_H

View File

@ -0,0 +1,327 @@
#include <dlfcn.h>
#include <assert.h>
#include <malloc.h>
#include <android/log.h>
#define TAG "freerdp_android_audiotrack"
#define LOGI(...) __android_log_print(ANDROID_LOG_DEBUG, TAG, __VA_ARGS__)
#include "audiotrack.h"
#define SIZE_OF_AUDIOTRACK 256
// _ZN7android11AudioSystem19getOutputFrameCountEPii
typedef int (*AudioSystem_getOutputFrameCount)(int *, int);
// _ZN7android11AudioSystem16getOutputLatencyEPji
typedef int (*AudioSystem_getOutputLatency)(unsigned int *, int);
// _ZN7android11AudioSystem21getOutputSamplingRateEPii
typedef int (*AudioSystem_getOutputSamplingRate)(int *, int);
// _ZN7android10AudioTrack16getMinFrameCountEPiij
typedef int (*AudioTrack_getMinFrameCount)(int *, int, unsigned int);
// _ZN7android10AudioTrackC1EijiiijPFviPvS1_ES1_ii
typedef void (*AudioTrack_ctor)(void *, int, unsigned int, int, int, int, unsigned int, void (*)(int, void *, void *), void *, int, int);
// _ZN7android10AudioTrackC1EijiiijPFviPvS1_ES1_i
typedef void (*AudioTrack_ctor_legacy)(void *, int, unsigned int, int, int, int, unsigned int, void (*)(int, void *, void *), void *, int);
// _ZN7android10AudioTrackD1Ev
typedef void (*AudioTrack_dtor)(void *);
// _ZNK7android10AudioTrack9initCheckEv
typedef int (*AudioTrack_initCheck)(void *);
typedef uint32_t (*AudioTrack_latency)(void *);
// _ZN7android10AudioTrack5startEv
typedef void (*AudioTrack_start)(void *);
// _ZN7android10AudioTrack4stopEv
typedef void (*AudioTrack_stop)(void *);
// _ZN7android10AudioTrack5writeEPKvj
typedef int (*AudioTrack_write)(void *, void const*, unsigned int);
// _ZN7android10AudioTrack5flushEv
typedef void (*AudioTrack_flush)(void *);
static void *libmedia;
static AudioSystem_getOutputFrameCount as_getOutputFrameCount;
static AudioSystem_getOutputLatency as_getOutputLatency;
static AudioSystem_getOutputSamplingRate as_getOutputSamplingRate;
static AudioTrack_getMinFrameCount at_getMinFrameCount;
static AudioTrack_ctor at_ctor;
static AudioTrack_ctor_legacy at_ctor_legacy;
static AudioTrack_dtor at_dtor;
static AudioTrack_initCheck at_initCheck;
static AudioTrack_latency at_latency;
static AudioTrack_start at_start;
static AudioTrack_stop at_stop;
static AudioTrack_write at_write;
static AudioTrack_flush at_flush;
class AndroidAudioTrack
{
private:
void* mAudioTrack;
public:
AndroidAudioTrack()
: mAudioTrack(NULL)
{
}
virtual ~AndroidAudioTrack()
{
close();
}
void close()
{
if (mAudioTrack) {
if (at_stop)
at_stop(mAudioTrack);
if (at_flush)
at_flush(mAudioTrack);
if (at_dtor)
at_dtor(mAudioTrack);
free(mAudioTrack);
mAudioTrack = NULL;
}
}
int set(int streamType, uint32_t sampleRate, int format, int channels)
{
int status;
int minFrameCount = 0;
int size = 0;
LOGI("streamTyp = %d, sampleRate = %d, format = %d, channels = %d\n", streamType, sampleRate, format, channels);
close();
if (at_getMinFrameCount) {
status = at_getMinFrameCount(&minFrameCount, streamType, sampleRate);
LOGI("at_getMinFrameCount %d, %d\n", minFrameCount, status);
}
//size = minFrameCount * (channels == CHANNEL_OUT_STEREO ? 2 : 1) * 4;
mAudioTrack = malloc(SIZE_OF_AUDIOTRACK);
*((uint32_t *) ((uint32_t)mAudioTrack + SIZE_OF_AUDIOTRACK - 4)) = 0xbaadbaad;
if (at_ctor) {
at_ctor(mAudioTrack, streamType, sampleRate, format, channels, size, 0, NULL, NULL, 0, 0);
} else if (at_ctor_legacy) {
at_ctor_legacy(mAudioTrack, streamType, sampleRate, format, channels, size, 0, NULL, NULL, 0);
} else {
LOGI("Cannot create AudioTrack!");
free(mAudioTrack);
mAudioTrack = NULL;
return -1;
}
assert( (*((uint32_t *) ((uint32_t)mAudioTrack + SIZE_OF_AUDIOTRACK - 4)) == 0xbaadbaad) );
/* And Init */
status = at_initCheck(mAudioTrack);
LOGI("at_initCheck = %d\n", status);
/* android 1.6 uses channel count instead of stream_type */
if (status != 0 && at_ctor_legacy) {
channels = (channels == CHANNEL_OUT_STEREO) ? 2 : 1;
at_ctor_legacy(mAudioTrack, streamType, sampleRate, format, channels, size, 0, NULL, NULL, 0);
status = at_initCheck(mAudioTrack);
LOGI("at_initCheck2 = %d\n", status);
}
if (status != 0) {
LOGI("Cannot create AudioTrack!");
free(mAudioTrack);
mAudioTrack = NULL;
}
return status;
}
uint32_t latency()
{
if (mAudioTrack && at_latency) {
return at_latency(mAudioTrack);
}
return 0;
}
int start()
{
if (mAudioTrack && at_start) {
at_start(mAudioTrack);
return ANDROID_AUDIOTRACK_RESULT_SUCCESS;
}
return ANDROID_AUDIOTRACK_RESULT_ERRNO;
}
int write(void* buffer, int size)
{
if (mAudioTrack && at_write) {
return at_write(mAudioTrack, buffer, size);
}
return ANDROID_AUDIOTRACK_RESULT_ERRNO;
}
int flush()
{
if (mAudioTrack && at_flush) {
at_flush(mAudioTrack);
return ANDROID_AUDIOTRACK_RESULT_SUCCESS;
}
return ANDROID_AUDIOTRACK_RESULT_ERRNO;
}
int stop()
{
if (mAudioTrack && at_stop) {
at_stop(mAudioTrack);
return ANDROID_AUDIOTRACK_RESULT_SUCCESS;
}
return ANDROID_AUDIOTRACK_RESULT_ERRNO;
}
int reload()
{
return ANDROID_AUDIOTRACK_RESULT_SUCCESS;
}
};
static void* InitLibrary()
{
/* DL Open libmedia */
void *p_library;
p_library = dlopen("libmedia.so", RTLD_NOW);
if (!p_library)
return NULL;
/* Register symbols */
as_getOutputFrameCount = (AudioSystem_getOutputFrameCount)(dlsym(p_library, "_ZN7android11AudioSystem19getOutputFrameCountEPii"));
as_getOutputLatency = (AudioSystem_getOutputLatency)(dlsym(p_library, "_ZN7android11AudioSystem16getOutputLatencyEPji"));
as_getOutputSamplingRate = (AudioSystem_getOutputSamplingRate)(dlsym(p_library, "_ZN7android11AudioSystem21getOutputSamplingRateEPii"));
at_getMinFrameCount = (AudioTrack_getMinFrameCount)(dlsym(p_library, "_ZN7android10AudioTrack16getMinFrameCountEPiij"));
at_ctor = (AudioTrack_ctor)(dlsym(p_library, "_ZN7android10AudioTrackC1EijiiijPFviPvS1_ES1_ii"));
at_ctor_legacy = (AudioTrack_ctor_legacy)(dlsym(p_library, "_ZN7android10AudioTrackC1EijiiijPFviPvS1_ES1_i"));
at_dtor = (AudioTrack_dtor)(dlsym(p_library, "_ZN7android10AudioTrackD1Ev"));
at_initCheck = (AudioTrack_initCheck)(dlsym(p_library, "_ZNK7android10AudioTrack9initCheckEv"));
at_latency = (AudioTrack_latency)(dlsym(p_library, "_ZNK7android10AudioTrack7latencyEv"));
at_start = (AudioTrack_start)(dlsym(p_library, "_ZN7android10AudioTrack5startEv"));
at_stop = (AudioTrack_stop)(dlsym(p_library, "_ZN7android10AudioTrack4stopEv"));
at_write = (AudioTrack_write)(dlsym(p_library, "_ZN7android10AudioTrack5writeEPKvj"));
at_flush = (AudioTrack_flush)(dlsym(p_library, "_ZN7android10AudioTrack5flushEv"));
LOGI("p_library : %p\n", p_library);
LOGI("as_getOutputFrameCount : %p\n", as_getOutputFrameCount);
LOGI("as_getOutputLatency : %p\n", as_getOutputLatency);
LOGI("as_getOutputSamplingRate : %p\n", as_getOutputSamplingRate);
LOGI("at_getMinFrameCount : %p\n", at_getMinFrameCount);
LOGI("at_ctor : %p\n", at_ctor);
LOGI("at_ctor_legacy : %p\n", at_ctor_legacy);
LOGI("at_dtor : %p\n", at_dtor);
LOGI("at_initCheck : %p\n", at_initCheck);
LOGI("at_latency : %p\n", at_latency);
LOGI("at_start : %p\n", at_start);
LOGI("at_stop : %p\n", at_stop);
LOGI("at_write : %p\n", at_write);
LOGI("at_flush : %p\n", at_flush);
/* We need the first 3 or the last 1 */
#if 0
if (!((as_getOutputFrameCount && as_getOutputLatency && as_getOutputSamplingRate)
|| at_getMinFrameCount)) {
dlclose(p_library);
return NULL;
}
#endif
// We need all the other Symbols
if (!((at_ctor || at_ctor_legacy) && at_dtor && at_initCheck &&
at_start && at_stop && at_write && at_flush)) {
dlclose(p_library);
return NULL;
}
return p_library;
}
int freerdp_android_at_open(AUDIO_DRIVER_HANDLE* outHandle)
{
int ret = ANDROID_AUDIOTRACK_RESULT_SUCCESS;
AndroidAudioTrack* audioTrack = new AndroidAudioTrack();
*outHandle = audioTrack;
return ret;
}
int freerdp_android_at_close(AUDIO_DRIVER_HANDLE handle)
{
AndroidAudioTrack* audioTrack = (AndroidAudioTrack*)handle;
if (!audioTrack)
return ANDROID_AUDIOTRACK_RESULT_ERRNO;
delete audioTrack;
return ANDROID_AUDIOTRACK_RESULT_SUCCESS;
}
int freerdp_android_at_set(AUDIO_DRIVER_HANDLE handle, int streamType, uint32_t sampleRate, int format, int channels)
{
AndroidAudioTrack* audioTrack = (AndroidAudioTrack*)handle;
if (!audioTrack)
return ANDROID_AUDIOTRACK_RESULT_ERRNO;
return audioTrack->set(streamType, sampleRate, format, channels);
}
int freerdp_android_at_set_volume(AUDIO_DRIVER_HANDLE handle, float left, float right)
{
AndroidAudioTrack* audioTrack = (AndroidAudioTrack*)handle;
if (!audioTrack)
return ANDROID_AUDIOTRACK_RESULT_ERRNO;
return 0;
}
uint32_t freerdp_android_at_latency(AUDIO_DRIVER_HANDLE handle)
{
AndroidAudioTrack* audioTrack = (AndroidAudioTrack*)handle;
if (!audioTrack)
return ANDROID_AUDIOTRACK_RESULT_ERRNO;
return audioTrack->latency();
}
int freerdp_android_at_start(AUDIO_DRIVER_HANDLE handle)
{
AndroidAudioTrack* audioTrack = (AndroidAudioTrack*)handle;
if (!audioTrack)
return ANDROID_AUDIOTRACK_RESULT_ERRNO;
return audioTrack->start();
}
int freerdp_android_at_write(AUDIO_DRIVER_HANDLE handle, void *buffer, int buffer_size)
{
AndroidAudioTrack* audioTrack = (AndroidAudioTrack*)handle;
if (!audioTrack)
return ANDROID_AUDIOTRACK_RESULT_ERRNO;
return audioTrack->write(buffer, buffer_size);
}
int freerdp_android_at_flush(AUDIO_DRIVER_HANDLE handle)
{
AndroidAudioTrack* audioTrack = (AndroidAudioTrack*)handle;
if (!audioTrack)
return ANDROID_AUDIOTRACK_RESULT_ERRNO;
return audioTrack->flush();
}
int freerdp_android_at_stop(AUDIO_DRIVER_HANDLE handle)
{
AndroidAudioTrack* audioTrack = (AndroidAudioTrack*)handle;
if (!audioTrack)
return ANDROID_AUDIOTRACK_RESULT_ERRNO;
return audioTrack->stop();
}
int freerdp_android_at_reload(AUDIO_DRIVER_HANDLE handle)
{
AndroidAudioTrack* audioTrack = (AndroidAudioTrack*)handle;
if (!audioTrack)
return ANDROID_AUDIOTRACK_RESULT_ERRNO;
return audioTrack->reload();
}
int freerdp_android_at_init_library()
{
if (libmedia == NULL)
{
libmedia = InitLibrary();
LOGI("loaded");
}
return 0;
}

View File

@ -0,0 +1,109 @@
/*
* Copyright (C) 2009 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef ANDROID_AUDIOTRACK_WRAPPER_H
#define ANDROID_AUDIOTRACK_WRAPPER_H
#include <stdint.h>
#include <jni.h>
#define ANDROID_AUDIOTRACK_RESULT_SUCCESS 0
#define ANDROID_AUDIOTRACK_RESULT_BAD_PARAMETER -1
#define ANDROID_AUDIOTRACK_RESULT_JNI_EXCEPTION -2
#define ANDROID_AUDIOTRACK_RESULT_ALLOCATION_FAILED -3
#define ANDROID_AUDIOTRACK_RESULT_ERRNO -4
enum stream_type {
DEFAULT =-1,
VOICE_CALL = 0,
SYSTEM = 1,
RING = 2,
MUSIC = 3,
ALARM = 4,
NOTIFICATION = 5,
BLUETOOTH_SCO = 6,
ENFORCED_AUDIBLE = 7, // Sounds that cannot be muted by user and must be routed to speaker
DTMF = 8,
TTS = 9,
NUM_STREAM_TYPES
};
enum {
NO_MORE_BUFFERS = 0x80000001,
STOPPED = 1
};
// Audio sub formats (see AudioSystem::audio_format).
enum pcm_sub_format {
PCM_SUB_16_BIT = 0x1, // must be 1 for backward compatibility
PCM_SUB_8_BIT = 0x2, // must be 2 for backward compatibility
};
// Audio format consists in a main format field (upper 8 bits) and a sub format field (lower 24 bits).
// The main format indicates the main codec type. The sub format field indicates options and parameters
// for each format. The sub format is mainly used for record to indicate for instance the requested bitrate
// or profile. It can also be used for certain formats to give informations not present in the encoded
// audio stream (e.g. octet alignement for AMR).
enum audio_format {
INVALID_FORMAT = -1,
FORMAT_DEFAULT = 0,
PCM = 0x00000000, // must be 0 for backward compatibility
MP3 = 0x01000000,
AMR_NB = 0x02000000,
AMR_WB = 0x03000000,
AAC = 0x04000000,
HE_AAC_V1 = 0x05000000,
HE_AAC_V2 = 0x06000000,
VORBIS = 0x07000000,
MAIN_FORMAT_MASK = 0xFF000000,
SUB_FORMAT_MASK = 0x00FFFFFF,
// Aliases
PCM_16_BIT = (PCM|PCM_SUB_16_BIT),
PCM_8_BIT = (PCM|PCM_SUB_8_BIT)
};
// Channel mask definitions must be kept in sync with JAVA values in /media/java/android/media/AudioFormat.java
enum audio_channels {
// output channels
CHANNEL_OUT_FRONT_LEFT = 0x4,
CHANNEL_OUT_FRONT_RIGHT = 0x8,
CHANNEL_OUT_MONO = CHANNEL_OUT_FRONT_LEFT,
CHANNEL_OUT_STEREO = (CHANNEL_OUT_FRONT_LEFT | CHANNEL_OUT_FRONT_RIGHT)
};
#ifdef __cplusplus
extern "C" {
#endif
typedef void* AUDIO_DRIVER_HANDLE;
int freerdp_android_at_init_library();
int freerdp_android_at_open(AUDIO_DRIVER_HANDLE* outHandle);
int freerdp_android_at_set(AUDIO_DRIVER_HANDLE handle, int streamType, uint32_t sampleRate, int format, int channels);
int freerdp_android_at_set_volume(AUDIO_DRIVER_HANDLE handle, float left, float right);
int freerdp_android_at_close(AUDIO_DRIVER_HANDLE handle);
uint32_t freerdp_android_at_latency(AUDIO_DRIVER_HANDLE handle);
int freerdp_android_at_start(AUDIO_DRIVER_HANDLE handle);
int freerdp_android_at_write(AUDIO_DRIVER_HANDLE handle, void *buffer, int buffer_size);
int freerdp_android_at_flush(AUDIO_DRIVER_HANDLE handle);
int freerdp_android_at_stop(AUDIO_DRIVER_HANDLE handle);
int freerdp_android_at_reload(AUDIO_DRIVER_HANDLE handle);
#ifdef __cplusplus
}
#endif
#endif

View File

@ -0,0 +1,308 @@
/**
* FreeRDP: A Remote Desktop Protocol client.
* Audio Output Virtual Channel
*
* Copyright 2009-2011 Jay Sorg
* Copyright 2010-2011 Vic Lee
* Copyright 2012 Felix Long
* Copyright 2013 Armin Novak
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <audiotrack.h>
#include <freerdp/types.h>
#include <freerdp/utils/svc_plugin.h>
#include <freerdp/codec/dsp.h>
#include "rdpsnd_main.h"
typedef struct rdpsnd_audiotrack_plugin rdpsndAudioTrackPlugin;
struct rdpsnd_audiotrack_plugin
{
rdpsndDevicePlugin device;
AUDIO_DRIVER_HANDLE out_handle;
UINT32 source_rate;
UINT32 actual_rate;
int format;
UINT32 source_channels;
UINT32 actual_channels;
int bytes_per_channel;
int wformat;
int block_size;
int latency;
FREERDP_DSP_CONTEXT* dsp_context;
};
static void rdpsnd_audiotrack_set_format(rdpsndDevicePlugin* device, AUDIO_FORMAT* format, int latency)
{
rdpsndAudioTrackPlugin* audiotrack = (rdpsndAudioTrackPlugin*)device;
if (format != NULL)
{
audiotrack->source_rate = format->nSamplesPerSec;
audiotrack->actual_rate = format->nSamplesPerSec;
audiotrack->source_channels = format->nChannels;
audiotrack->actual_channels = format->nChannels;
switch (format->wFormatTag)
{
case 1: /* PCM */
switch (format->wBitsPerSample)
{
case 8:
audiotrack->format = PCM_8_BIT;
audiotrack->bytes_per_channel = 1;
break;
case 16:
audiotrack->format = PCM_16_BIT;
audiotrack->bytes_per_channel = 2;
break;
}
break;
case 2: /* MS ADPCM */
case 0x11: /* IMA ADPCM */
audiotrack->format = PCM_16_BIT;
audiotrack->bytes_per_channel = 2;
break;
}
audiotrack->wformat = format->wFormatTag;
audiotrack->block_size = format->nBlockAlign;
}
audiotrack->latency = latency;
freerdp_android_at_set(audiotrack->out_handle,
MUSIC,
audiotrack->actual_rate,
audiotrack->format,
audiotrack->actual_channels == 2 ? CHANNEL_OUT_STEREO : CHANNEL_OUT_MONO);
}
static void rdpsnd_audiotrack_open(rdpsndDevicePlugin* device, AUDIO_FORMAT* format, int latency)
{
rdpsndAudioTrackPlugin* audiotrack = (rdpsndAudioTrackPlugin*)device;
int error;
if (audiotrack->out_handle != 0)
return;
DEBUG_SVC("opening");
error = freerdp_android_at_open(&audiotrack->out_handle);
if (error < 0)
{
DEBUG_WARN("freerdp_android_at_open failed");
}
else
{
freerdp_dsp_context_reset_adpcm(audiotrack->dsp_context);
rdpsnd_audiotrack_set_format(device, format, latency);
}
}
static void rdpsnd_audiotrack_close(rdpsndDevicePlugin* device)
{
rdpsndAudioTrackPlugin* audiotrack = (rdpsndAudioTrackPlugin*)device;
if (audiotrack->out_handle != 0)
{
DEBUG_SVC("close");
freerdp_android_at_close(audiotrack->out_handle);
audiotrack->out_handle = 0;
}
}
static void rdpsnd_audiotrack_free(rdpsndDevicePlugin* device)
{
rdpsndAudioTrackPlugin* audiotrack = (rdpsndAudioTrackPlugin*)device;
rdpsnd_audiotrack_close(device);
freerdp_dsp_context_free(audiotrack->dsp_context);
free(audiotrack);
}
static BOOL rdpsnd_audiotrack_format_supported(rdpsndDevicePlugin* device, AUDIO_FORMAT* format)
{
switch (format->wFormatTag)
{
case 1: /* PCM */
if (format->cbSize == 0 &&
format->nSamplesPerSec <= 48000 &&
(format->wBitsPerSample == 8 || format->wBitsPerSample == 16) &&
(format->nChannels == 1 || format->nChannels == 2))
{
return TRUE;
}
break;
case 2: /* MS ADPCM */
case 0x11: /* IMA ADPCM */
if (format->nSamplesPerSec <= 48000 &&
format->wBitsPerSample == 4 &&
(format->nChannels == 1 || format->nChannels == 2))
{
return TRUE;
}
break;
}
return FALSE;
}
static void rdpsnd_audiotrack_set_volume(rdpsndDevicePlugin* device, UINT32 value)
{
rdpsndAudioTrackPlugin* audiotrack = (rdpsndAudioTrackPlugin*)device;
float left;
float right;
if (audiotrack->out_handle == 0)
return;
left = ((value & 0xFFFF) * 1.0) / 0xFFFF;
right = (((value >> 16) & 0xFFFF) * 1.0) / 0xFFFF;
freerdp_android_at_set_volume(audiotrack->out_handle, left, right);
}
static void rdpsnd_audiotrack_play(rdpsndDevicePlugin* device, BYTE* data, int size)
{
rdpsndAudioTrackPlugin* audiotrack = (rdpsndAudioTrackPlugin*)device;
BYTE* src;
int len;
int error;
int frames;
int rbytes_per_frame;
int sbytes_per_frame;
BYTE* pindex;
BYTE* end;
if (audiotrack->out_handle == 0)
return;
if (audiotrack->wformat == 2)
{
audiotrack->dsp_context->decode_ms_adpcm(audiotrack->dsp_context,
data, size, audiotrack->source_channels, audiotrack->block_size);
size = audiotrack->dsp_context->adpcm_size;
src = audiotrack->dsp_context->adpcm_buffer;
}
else if (audiotrack->wformat == 0x11)
{
audiotrack->dsp_context->decode_ima_adpcm(audiotrack->dsp_context,
data, size, audiotrack->source_channels, audiotrack->block_size);
size = audiotrack->dsp_context->adpcm_size;
src = audiotrack->dsp_context->adpcm_buffer;
}
else
{
src = data;
}
sbytes_per_frame = audiotrack->source_channels * audiotrack->bytes_per_channel;
rbytes_per_frame = audiotrack->actual_channels * audiotrack->bytes_per_channel;
if ((size % sbytes_per_frame) != 0)
{
DEBUG_WARN("error len mod");
return;
}
if ((audiotrack->source_rate == audiotrack->actual_rate) &&
(audiotrack->source_channels == audiotrack->actual_channels))
{
}
else
{
audiotrack->dsp_context->resample(audiotrack->dsp_context, src, audiotrack->bytes_per_channel,
audiotrack->source_channels, audiotrack->source_rate, size / sbytes_per_frame,
audiotrack->actual_channels, audiotrack->actual_rate);
frames = audiotrack->dsp_context->resampled_frames;
DEBUG_SVC("resampled %d frames at %d to %d frames at %d",
size / sbytes_per_frame, audiotrack->source_rate, frames, audiotrack->actual_rate);
size = frames * rbytes_per_frame;
src = audiotrack->dsp_context->resampled_buffer;
}
pindex = src;
end = pindex + size;
while (pindex < end)
{
len = end - pindex;
error = freerdp_android_at_write(audiotrack->out_handle, pindex, len);
if (error < 0)
{
DEBUG_WARN("error %d", error);
freerdp_android_at_close(audiotrack->out_handle);
audiotrack->out_handle = 0;
rdpsnd_audiotrack_open(device, NULL, audiotrack->latency);
break;
}
pindex += error;
}
}
static void rdpsnd_audiotrack_start(rdpsndDevicePlugin* device)
{
rdpsndAudioTrackPlugin* audiotrack = (rdpsndAudioTrackPlugin*)device;
if (audiotrack->out_handle == 0)
return;
freerdp_android_at_start(audiotrack->out_handle);
}
#ifdef STATIC_CHANNELS
#define freerdp_rdpsnd_client_subsystem_entry audiotrack_freerdp_rdpsnd_client_subsystem_entry
#endif
int freerdp_rdpsnd_client_subsystem_entry(PFREERDP_RDPSND_DEVICE_ENTRY_POINTS pEntryPoints)
{
rdpsndAudioTrackPlugin* audiotrack;
freerdp_android_at_init_library();
audiotrack = malloc(sizeof(rdpsndAudioTrackPlugin));
audiotrack->device.Open = rdpsnd_audiotrack_open;
audiotrack->device.FormatSupported = rdpsnd_audiotrack_format_supported;
audiotrack->device.SetFormat = rdpsnd_audiotrack_set_format;
audiotrack->device.SetVolume = rdpsnd_audiotrack_set_volume;
audiotrack->device.Play = rdpsnd_audiotrack_play;
audiotrack->device.Start = rdpsnd_audiotrack_start;
audiotrack->device.Close = rdpsnd_audiotrack_close;
audiotrack->device.Free = rdpsnd_audiotrack_free;
audiotrack->out_handle = 0;
audiotrack->source_rate = 22050;
audiotrack->actual_rate = 22050;
audiotrack->format = PCM_16_BIT;
audiotrack->source_channels = 2;
audiotrack->actual_channels = 2;
audiotrack->bytes_per_channel = 2;
audiotrack->dsp_context = freerdp_dsp_context_new();
pEntryPoints->pRegisterRdpsndDevice(pEntryPoints->rdpsnd, (rdpsndDevicePlugin*)audiotrack);
return 0;
}