mirror of
https://github.com/steve-m/hsdaoh.git
synced 2025-12-10 07:44:41 +01:00
add FLAC encoding support
This commit is contained in:
parent
c9b0837c92
commit
d0984dac17
9 changed files with 243 additions and 40 deletions
|
|
@ -88,6 +88,16 @@ else()
|
||||||
set(LIBUVC_INCLUDE_DIRS "" CACHE STRING "manual libuvc includepath")
|
set(LIBUVC_INCLUDE_DIRS "" CACHE STRING "manual libuvc includepath")
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
if(PKG_CONFIG_FOUND)
|
||||||
|
pkg_check_modules(LIBFLAC flac IMPORTED_TARGET)
|
||||||
|
if(LIBFLAC_LINK_LIBRARIES)
|
||||||
|
set(LIBFLAC_LIBRARIES "${LIBFLAC_LINK_LIBRARIES}")
|
||||||
|
endif()
|
||||||
|
else()
|
||||||
|
set(LIBFLAC_LIBRARIES "" CACHE STRING "manual FLAC path")
|
||||||
|
set(LIBFLAC_INCLUDE_DIRS "" CACHE STRING "manual FLAC includepath")
|
||||||
|
endif()
|
||||||
|
|
||||||
if(MSVC)
|
if(MSVC)
|
||||||
set(THREADS_PTHREADS_LIBRARY "" CACHE STRING "manual pthread-win32 path")
|
set(THREADS_PTHREADS_LIBRARY "" CACHE STRING "manual pthread-win32 path")
|
||||||
set(THREADS_PTHREADS_INCLUDE_DIR "" CACHE STRING "manual pthread-win32 includepath")
|
set(THREADS_PTHREADS_INCLUDE_DIR "" CACHE STRING "manual pthread-win32 includepath")
|
||||||
|
|
@ -102,6 +112,9 @@ endif()
|
||||||
if(PKG_CONFIG_FOUND AND NOT LIBUVC_FOUND)
|
if(PKG_CONFIG_FOUND AND NOT LIBUVC_FOUND)
|
||||||
message(FATAL_ERROR "LibUVC required to compile hsdaoh")
|
message(FATAL_ERROR "LibUVC required to compile hsdaoh")
|
||||||
endif()
|
endif()
|
||||||
|
if(PKG_CONFIG_FOUND AND NOT LIBFLAC_FOUND)
|
||||||
|
message(FATAL_ERROR "LibFLAC required to compile hsdaoh")
|
||||||
|
endif()
|
||||||
if(NOT THREADS_FOUND)
|
if(NOT THREADS_FOUND)
|
||||||
message(FATAL_ERROR "pthreads(-win32) required to compile hsdaoh")
|
message(FATAL_ERROR "pthreads(-win32) required to compile hsdaoh")
|
||||||
endif()
|
endif()
|
||||||
|
|
@ -165,6 +178,14 @@ FOREACH(lib ${LIBUVC_LIBRARY_DIRS})
|
||||||
LIST(APPEND hsdaoh_PC_LIBS "-L${lib}")
|
LIST(APPEND hsdaoh_PC_LIBS "-L${lib}")
|
||||||
ENDFOREACH(lib)
|
ENDFOREACH(lib)
|
||||||
|
|
||||||
|
FOREACH(inc ${LIBFLAC_INCLUDEDIR})
|
||||||
|
LIST(APPEND hsdaoh_PC_CFLAGS "-I${inc}")
|
||||||
|
ENDFOREACH(inc)
|
||||||
|
|
||||||
|
FOREACH(lib ${LIBFLAC_LIBRARY_DIRS})
|
||||||
|
LIST(APPEND hsdaoh_PC_LIBS "-L${lib}")
|
||||||
|
ENDFOREACH(lib)
|
||||||
|
|
||||||
# use space-separation format for the pc file
|
# use space-separation format for the pc file
|
||||||
STRING(REPLACE ";" " " hsdaoh_PC_CFLAGS "${hsdaoh_PC_CFLAGS}")
|
STRING(REPLACE ";" " " hsdaoh_PC_CFLAGS "${hsdaoh_PC_CFLAGS}")
|
||||||
STRING(REPLACE ";" " " hsdaoh_PC_LIBS "${hsdaoh_PC_LIBS}")
|
STRING(REPLACE ";" " " hsdaoh_PC_LIBS "${hsdaoh_PC_LIBS}")
|
||||||
|
|
|
||||||
|
|
@ -42,7 +42,7 @@ There is one additional dependency to abstract the video input however (libuvc).
|
||||||
|
|
||||||
To install the build dependencies on a distribution based on Debian (e.g. Ubuntu), run the following command:
|
To install the build dependencies on a distribution based on Debian (e.g. Ubuntu), run the following command:
|
||||||
|
|
||||||
sudo apt-get install build-essential cmake pkgconf libusb-1.0-0-dev libuvc-dev
|
sudo apt-get install build-essential cmake pkgconf libusb-1.0-0-dev libuvc-dev libflac-dev
|
||||||
|
|
||||||
To build hsdaoh:
|
To build hsdaoh:
|
||||||
|
|
||||||
|
|
@ -80,7 +80,7 @@ pacman -Suy
|
||||||
|
|
||||||
# Install the required dependencies:
|
# Install the required dependencies:
|
||||||
pacman -S git zip mingw-w64-x86_64-libusb mingw-w64-x86_64-libwinpthread mingw-w64-x86_64-cc \
|
pacman -S git zip mingw-w64-x86_64-libusb mingw-w64-x86_64-libwinpthread mingw-w64-x86_64-cc \
|
||||||
mingw-w64-x86_64-gcc-libs mingw-w64-x86_64-cmake mingw-w64-x86_64-ninja
|
mingw-w64-x86_64-gcc-libs mingw-w64-x86_64-cmake mingw-w64-x86_64-ninja mingw-w64-x86_64-flac
|
||||||
```
|
```
|
||||||
|
|
||||||
#### Build libuvc
|
#### Build libuvc
|
||||||
|
|
@ -88,7 +88,7 @@ mingw-w64-x86_64-gcc-libs mingw-w64-x86_64-cmake mingw-w64-x86_64-ninja
|
||||||
# Clone the repository:
|
# Clone the repository:
|
||||||
git clone https://github.com/steve-m/libuvc.git
|
git clone https://github.com/steve-m/libuvc.git
|
||||||
mkdir libuvc/build && cd libuvc/build
|
mkdir libuvc/build && cd libuvc/build
|
||||||
cmake ../ -DCMAKE_INSTALL_PREFIX:PATH=/mingw64
|
cmake ../ -DCMAKE_POLICY_VERSION_MINIMUM=3.10 -DCMAKE_INSTALL_PREFIX:PATH=/mingw64
|
||||||
cmake --build .
|
cmake --build .
|
||||||
cmake --install .
|
cmake --install .
|
||||||
```
|
```
|
||||||
|
|
@ -101,7 +101,7 @@ mkdir hsdaoh/build && cd hsdaoh/build
|
||||||
cmake ../
|
cmake ../
|
||||||
cmake --build .
|
cmake --build .
|
||||||
# Gather all files required for release
|
# Gather all files required for release
|
||||||
zip -j hsdaoh_win_release.zip src/*.exe src/*.dll /mingw64/bin/libusb-1.0.dll /mingw64/bin/libuvc.dll /mingw64/bin/libwinpthread-1.dll
|
zip -j hsdaoh_win_release.zip src/*.exe src/*.dll /mingw64/bin/libusb-1.0.dll /mingw64/bin/libuvc.dll /mingw64/bin/libwinpthread-1.dll /mingw64/bin/libFLAC.dll /mingw64/bin/libogg-0.dll
|
||||||
```
|
```
|
||||||
|
|
||||||
### Mac OS X
|
### Mac OS X
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
include(FindPkgConfig)
|
include(FindPkgConfig)
|
||||||
pkg_check_modules(LIBUSB libusb-1.0 IMPORTED_TARGET)
|
pkg_check_modules(LIBUSB libusb-1.0 IMPORTED_TARGET)
|
||||||
pkg_check_modules(LIBUVC libuvc IMPORTED_TARGET)
|
pkg_check_modules(LIBUVC libuvc IMPORTED_TARGET)
|
||||||
|
pkg_check_modules(LIBFLAC flac IMPORTED_TARGET)
|
||||||
|
|
||||||
get_filename_component(HSDAOH_CMAKE_DIR "${CMAKE_CURRENT_LIST_FILE}" PATH)
|
get_filename_component(HSDAOH_CMAKE_DIR "${CMAKE_CURRENT_LIST_FILE}" PATH)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -41,6 +41,11 @@ typedef struct hsdaoh_data_info {
|
||||||
unsigned char *buf;
|
unsigned char *buf;
|
||||||
size_t len; /* buffer length */
|
size_t len; /* buffer length */
|
||||||
uint16_t stream_id;
|
uint16_t stream_id;
|
||||||
|
uint32_t srate;
|
||||||
|
uint8_t nchans;
|
||||||
|
uint8_t bits_per_samp;
|
||||||
|
bool is_signed;
|
||||||
|
bool is_float;
|
||||||
bool device_error; /* device error happened, terminate application */
|
bool device_error; /* device error happened, terminate application */
|
||||||
} hsdaoh_data_info_t;
|
} hsdaoh_data_info_t;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -12,6 +12,7 @@ struct llist {
|
||||||
size_t len;
|
size_t len;
|
||||||
uint16_t sid;
|
uint16_t sid;
|
||||||
uint16_t format;
|
uint16_t format;
|
||||||
|
uint32_t srate;
|
||||||
struct llist *next;
|
struct llist *next;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -85,6 +85,7 @@ add_executable(hsdaoh_test hsdaoh_test.c)
|
||||||
set(INSTALL_TARGETS hsdaoh hsdaoh_static hsdaoh_file hsdaoh_tcp hsdaoh_test)
|
set(INSTALL_TARGETS hsdaoh hsdaoh_static hsdaoh_file hsdaoh_tcp hsdaoh_test)
|
||||||
|
|
||||||
target_link_libraries(hsdaoh_file hsdaoh
|
target_link_libraries(hsdaoh_file hsdaoh
|
||||||
|
${LIBFLAC_LIBRARIES}
|
||||||
# ${LIBUSB_LIBRARIES}
|
# ${LIBUSB_LIBRARIES}
|
||||||
${CMAKE_THREAD_LIBS_INIT}
|
${CMAKE_THREAD_LIBS_INIT}
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -53,6 +53,10 @@ static inline void hsdaoh_16bit_to_float(hsdaoh_dev_t *dev, hsdaoh_data_info_t *
|
||||||
|
|
||||||
data_info->buf = (uint8_t *)floats;
|
data_info->buf = (uint8_t *)floats;
|
||||||
data_info->len = j * sizeof(float);
|
data_info->len = j * sizeof(float);
|
||||||
|
data_info->bits_per_samp = 32;
|
||||||
|
data_info->nchans = 1;
|
||||||
|
data_info->is_signed = true;
|
||||||
|
data_info->is_float = true;
|
||||||
dev->cb(data_info);
|
dev->cb(data_info);
|
||||||
|
|
||||||
free(floats);
|
free(floats);
|
||||||
|
|
@ -81,6 +85,11 @@ void hsdaoh_unpack_pio_12bit(hsdaoh_dev_t *dev, hsdaoh_data_info_t *data_info)
|
||||||
} else {
|
} else {
|
||||||
data_info->buf = (uint8_t *)out;
|
data_info->buf = (uint8_t *)out;
|
||||||
data_info->len = j * sizeof(uint16_t);
|
data_info->len = j * sizeof(uint16_t);
|
||||||
|
data_info->bits_per_samp = 12;
|
||||||
|
data_info->nchans = 1;
|
||||||
|
data_info->is_signed = false;
|
||||||
|
data_info->is_float = false;
|
||||||
|
|
||||||
dev->cb(data_info);
|
dev->cb(data_info);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -112,11 +121,18 @@ void hsdaoh_unpack_pio_12bit_dual(hsdaoh_dev_t *dev, hsdaoh_data_info_t *data_in
|
||||||
out16_2[i] = out[i] & 0x0fff;
|
out16_2[i] = out[i] & 0x0fff;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
data_info->bits_per_samp = 12;
|
||||||
|
data_info->nchans = 1;
|
||||||
|
|
||||||
if (dev->output_float) {
|
if (dev->output_float) {
|
||||||
hsdaoh_16bit_to_float(dev, data_info, out16_1, i, 2047.5, true);
|
hsdaoh_16bit_to_float(dev, data_info, out16_1, i, 2047.5, true);
|
||||||
} else {
|
} else {
|
||||||
data_info->buf = (uint8_t *)out16_1;
|
data_info->buf = (uint8_t *)out16_1;
|
||||||
data_info->len = i * sizeof(uint16_t);
|
data_info->len = i * sizeof(uint16_t);
|
||||||
|
data_info->bits_per_samp = 12;
|
||||||
|
data_info->nchans = 1;
|
||||||
|
data_info->is_signed = false;
|
||||||
|
data_info->is_float = false;
|
||||||
|
|
||||||
dev->cb(data_info);
|
dev->cb(data_info);
|
||||||
}
|
}
|
||||||
|
|
@ -157,6 +173,10 @@ void hsdaoh_unpack_pio_8bit_iq(hsdaoh_dev_t *dev, hsdaoh_data_info_t *data_info)
|
||||||
} else {
|
} else {
|
||||||
data_info->buf = (uint8_t *)in;
|
data_info->buf = (uint8_t *)in;
|
||||||
data_info->len = inlen;
|
data_info->len = inlen;
|
||||||
|
data_info->bits_per_samp = 8;
|
||||||
|
data_info->nchans = 2;
|
||||||
|
data_info->is_signed = false;
|
||||||
|
data_info->is_float = false;
|
||||||
|
|
||||||
dev->cb(data_info);
|
dev->cb(data_info);
|
||||||
}
|
}
|
||||||
|
|
@ -202,6 +222,10 @@ void hsdaoh_unpack_pio_10bit_iq(hsdaoh_dev_t *dev, hsdaoh_data_info_t *data_info
|
||||||
} else {
|
} else {
|
||||||
data_info->buf = (uint8_t *)iq_samps;
|
data_info->buf = (uint8_t *)iq_samps;
|
||||||
data_info->len = out_samps * sizeof(uint16_t);
|
data_info->len = out_samps * sizeof(uint16_t);
|
||||||
|
data_info->bits_per_samp = 10;
|
||||||
|
data_info->nchans = 2;
|
||||||
|
data_info->is_signed = false;
|
||||||
|
data_info->is_float = false;
|
||||||
|
|
||||||
dev->cb(data_info);
|
dev->cb(data_info);
|
||||||
}
|
}
|
||||||
|
|
@ -215,14 +239,24 @@ void hsdaoh_unpack_pio_pcm1802_audio(hsdaoh_dev_t *dev, hsdaoh_data_info_t *data
|
||||||
uint32_t *in = (uint32_t *)data_info->buf;
|
uint32_t *in = (uint32_t *)data_info->buf;
|
||||||
size_t inlen = data_info->len / sizeof(uint32_t);
|
size_t inlen = data_info->len / sizeof(uint32_t);
|
||||||
|
|
||||||
/* convert from S24LE to S32LE */
|
/* output as S24_3LE */
|
||||||
for (unsigned int i = 0; i < inlen; i++)
|
uint8_t *out = malloc(inlen * 3);
|
||||||
in[i] <<= 8;
|
int j = 0;
|
||||||
|
|
||||||
data_info->buf = (uint8_t *)in;
|
for (unsigned int i = 0; i < inlen; i++) {
|
||||||
data_info->len = inlen * sizeof(uint32_t);
|
out[j++] = in[i] & 0xff;
|
||||||
|
out[j++] = (in[i] >> 8) & 0xff;
|
||||||
|
out[j++] = (in[i] >> 16) & 0xff;
|
||||||
|
}
|
||||||
|
|
||||||
|
data_info->buf = (uint8_t *)out;
|
||||||
|
data_info->len = j;
|
||||||
|
data_info->bits_per_samp = 24;
|
||||||
|
data_info->nchans = 2;
|
||||||
|
data_info->is_signed = true;
|
||||||
|
data_info->is_float = false;
|
||||||
dev->cb(data_info);
|
dev->cb(data_info);
|
||||||
|
free(out);
|
||||||
}
|
}
|
||||||
|
|
||||||
void hsdaoh_unpack_fpga_12bit_dual(hsdaoh_dev_t *dev, hsdaoh_data_info_t *data_info)
|
void hsdaoh_unpack_fpga_12bit_dual(hsdaoh_dev_t *dev, hsdaoh_data_info_t *data_info)
|
||||||
|
|
@ -250,6 +284,10 @@ void hsdaoh_unpack_fpga_12bit_dual(hsdaoh_dev_t *dev, hsdaoh_data_info_t *data_i
|
||||||
} else {
|
} else {
|
||||||
data_info->buf = (uint8_t *)out16_1;
|
data_info->buf = (uint8_t *)out16_1;
|
||||||
data_info->len = j * sizeof(uint16_t);
|
data_info->len = j * sizeof(uint16_t);
|
||||||
|
data_info->bits_per_samp = 12;
|
||||||
|
data_info->nchans = 1;
|
||||||
|
data_info->is_signed = false;
|
||||||
|
data_info->is_float = false;
|
||||||
|
|
||||||
dev->cb(data_info);
|
dev->cb(data_info);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -35,15 +35,23 @@
|
||||||
#define usleep(t) Sleep((t)/1000)
|
#define usleep(t) Sleep((t)/1000)
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#include "FLAC/metadata.h"
|
||||||
|
#include "FLAC/stream_encoder.h"
|
||||||
|
|
||||||
#include "hsdaoh.h"
|
#include "hsdaoh.h"
|
||||||
|
|
||||||
#define FD_NUMS 4
|
#define FD_NUMS 4
|
||||||
|
|
||||||
static int do_exit = 0;
|
static bool do_exit = false;
|
||||||
static hsdaoh_dev_t *dev = NULL;
|
static hsdaoh_dev_t *dev = NULL;
|
||||||
|
static uint32_t flac_level = 5;
|
||||||
|
static uint32_t flac_nthreads = 4;
|
||||||
|
|
||||||
typedef struct file_ctx {
|
typedef struct file_ctx {
|
||||||
FILE *files[FD_NUMS];
|
FILE *files[FD_NUMS];
|
||||||
|
bool use_flac[FD_NUMS];
|
||||||
|
FLAC__StreamEncoder *encoder[FD_NUMS];
|
||||||
|
FLAC__StreamMetadata *seektable[FD_NUMS];
|
||||||
} file_ctx_t;
|
} file_ctx_t;
|
||||||
|
|
||||||
void usage(void)
|
void usage(void)
|
||||||
|
|
@ -52,9 +60,14 @@ void usage(void)
|
||||||
"hsdaoh_file, HDMI data acquisition tool\n\n"
|
"hsdaoh_file, HDMI data acquisition tool\n\n"
|
||||||
"Usage:\n"
|
"Usage:\n"
|
||||||
"\t[-d device_index (default: 0)]\n"
|
"\t[-d device_index (default: 0)]\n"
|
||||||
"\t[-b maximum number of buffers (default: 16)]\n"
|
"\t[-b maximum number of buffers (default: 96)]\n"
|
||||||
"\t[-0 to -3 filename of steam 0 to stream 3 (a '-' dumps samples to stdout)]\n"
|
"\t[-l FLAC compression level (default: 5)]\n"
|
||||||
"\tfilename (of stream 0) (a '-' dumps samples to stdout)\n\n");
|
#ifdef FLAC__STREAM_ENCODER_SET_NUM_THREADS_OK
|
||||||
|
"\t[-t number of threads for FLAC encoding (default: 4)]\n"
|
||||||
|
#endif
|
||||||
|
"\t[-0 to -3 filename of stream 0 to stream 3 (a '-' dumps samples to stdout)]\n"
|
||||||
|
"\tfilename (of stream 0) (a '-' dumps samples to stdout)\n\n"
|
||||||
|
"\tFilenames with the extension .flac will enable FLAC encoding\n\n");
|
||||||
exit(1);
|
exit(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -64,7 +77,7 @@ sighandler(int signum)
|
||||||
{
|
{
|
||||||
if (CTRL_C_EVENT == signum) {
|
if (CTRL_C_EVENT == signum) {
|
||||||
fprintf(stderr, "Signal caught, exiting!\n");
|
fprintf(stderr, "Signal caught, exiting!\n");
|
||||||
do_exit = 1;
|
do_exit = true;
|
||||||
hsdaoh_stop_stream(dev);
|
hsdaoh_stop_stream(dev);
|
||||||
return TRUE;
|
return TRUE;
|
||||||
}
|
}
|
||||||
|
|
@ -75,7 +88,7 @@ static void sighandler(int signum)
|
||||||
{
|
{
|
||||||
signal(SIGPIPE, SIG_IGN);
|
signal(SIGPIPE, SIG_IGN);
|
||||||
fprintf(stderr, "Signal caught, exiting!\n");
|
fprintf(stderr, "Signal caught, exiting!\n");
|
||||||
do_exit = 1;
|
do_exit = true;
|
||||||
hsdaoh_stop_stream(dev);
|
hsdaoh_stop_stream(dev);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
@ -97,6 +110,102 @@ static void hsdaoh_callback(hsdaoh_data_info_t *data_info)
|
||||||
if (!file)
|
if (!file)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
FLAC__StreamEncoder *encoder = f->encoder[data_info->stream_id];
|
||||||
|
/* allocate FLAC encoder if required */
|
||||||
|
if (f->use_flac[data_info->stream_id] && !encoder) {
|
||||||
|
FLAC__bool ret = true;
|
||||||
|
FLAC__StreamEncoderInitStatus init_status;
|
||||||
|
|
||||||
|
if ((encoder = FLAC__stream_encoder_new()) == NULL) {
|
||||||
|
fprintf(stderr, "ERROR: failed allocating FLAC encoder\n");
|
||||||
|
do_exit = true;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret &= FLAC__stream_encoder_set_verify(encoder, false);
|
||||||
|
ret &= FLAC__stream_encoder_set_compression_level(encoder, flac_level);
|
||||||
|
ret &= FLAC__stream_encoder_set_sample_rate(encoder,
|
||||||
|
data_info->srate > FLAC__MAX_SAMPLE_RATE ?
|
||||||
|
data_info->srate/1000 : data_info->srate);
|
||||||
|
|
||||||
|
ret &= FLAC__stream_encoder_set_channels(encoder, data_info->nchans);
|
||||||
|
ret &= FLAC__stream_encoder_set_bits_per_sample(encoder, data_info->bits_per_samp);
|
||||||
|
ret &= FLAC__stream_encoder_set_total_samples_estimate(encoder, 0);
|
||||||
|
ret &= FLAC__stream_encoder_set_streamable_subset(encoder, false);
|
||||||
|
|
||||||
|
#ifdef FLAC__STREAM_ENCODER_SET_NUM_THREADS_OK
|
||||||
|
if (FLAC__stream_encoder_set_num_threads(encoder, flac_nthreads) != FLAC__STREAM_ENCODER_SET_NUM_THREADS_OK)
|
||||||
|
ret = false;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
if (!ret) {
|
||||||
|
fprintf(stderr, "ERROR: failed initializing FLAC encoder\n");
|
||||||
|
do_exit = true;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
init_status = FLAC__stream_encoder_init_FILE(encoder, f->files[data_info->stream_id], NULL, NULL);
|
||||||
|
if (init_status != FLAC__STREAM_ENCODER_INIT_STATUS_OK) {
|
||||||
|
fprintf(stderr, "ERROR: failed initializing FLAC encoder: %s\n", FLAC__StreamEncoderInitStatusString[init_status]);
|
||||||
|
do_exit = true;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
f->encoder[data_info->stream_id] = encoder;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (f->use_flac[data_info->stream_id]) {
|
||||||
|
/* write FLAC output */
|
||||||
|
FLAC__bool ok = false;
|
||||||
|
FLAC__int32 offset = data_info->is_signed ? 0 : 1 << (data_info->bits_per_samp - 1);
|
||||||
|
int bytes_per_samp = ((data_info->bits_per_samp - 1) / 8) + 1;
|
||||||
|
int nsamps = len / bytes_per_samp;
|
||||||
|
FLAC__int32 *out = malloc(nsamps * sizeof(FLAC__int32));
|
||||||
|
int i = 0;
|
||||||
|
|
||||||
|
if (bytes_per_samp == 1) {
|
||||||
|
/* data encoded in uint8 */
|
||||||
|
uint8_t *dat = (uint8_t *)data_info->buf;
|
||||||
|
|
||||||
|
for (; i < nsamps; i++)
|
||||||
|
out[i] = dat[i] - offset;
|
||||||
|
} else if (bytes_per_samp == 2) {
|
||||||
|
/* data encoded in uint16 */
|
||||||
|
uint16_t *dat = (uint16_t *)data_info->buf;
|
||||||
|
|
||||||
|
for (; i < nsamps; i++)
|
||||||
|
out[i] = dat[i] - offset;
|
||||||
|
} else if (bytes_per_samp == 3) {
|
||||||
|
/* data encoded in 3 * uint8 */
|
||||||
|
uint8_t *dat = (uint8_t *)data_info->buf;
|
||||||
|
int j = 0;
|
||||||
|
|
||||||
|
for (; i < nsamps; i++) {
|
||||||
|
/* convert S24_3LE and take care of sign extension */
|
||||||
|
FLAC__int32 samp = (FLAC__int32)((dat[j+2] << 24) | (dat[j+1] << 16) | (dat[j] << 8));
|
||||||
|
//samp /= 256;
|
||||||
|
samp >>= (32 - data_info->bits_per_samp);
|
||||||
|
j += 3;
|
||||||
|
out[i] = samp - offset;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
/* data encoded in uint32 */
|
||||||
|
uint32_t *dat = (uint32_t *)data_info->buf;
|
||||||
|
|
||||||
|
for (; i < nsamps; i++)
|
||||||
|
out[i] = dat[i] - offset;
|
||||||
|
}
|
||||||
|
|
||||||
|
ok = FLAC__stream_encoder_process_interleaved(f->encoder[data_info->stream_id], out, nsamps / data_info->nchans);
|
||||||
|
free(out);
|
||||||
|
|
||||||
|
if (!ok && encoder) {
|
||||||
|
fprintf(stderr, "ERROR: () FLAC encoder could not process data: %s\n",
|
||||||
|
FLAC__StreamEncoderStateString[FLAC__stream_encoder_get_state(encoder)]);
|
||||||
|
hsdaoh_stop_stream(dev);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
/* write raw file output */
|
||||||
while (nbytes < len) {
|
while (nbytes < len) {
|
||||||
nbytes += fwrite(data_info->buf + nbytes, 1, len - nbytes, file);
|
nbytes += fwrite(data_info->buf + nbytes, 1, len - nbytes, file);
|
||||||
|
|
||||||
|
|
@ -107,6 +216,7 @@ static void hsdaoh_callback(hsdaoh_data_info_t *data_info)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
int main(int argc, char **argv)
|
int main(int argc, char **argv)
|
||||||
{
|
{
|
||||||
|
|
@ -116,13 +226,13 @@ int main(int argc, char **argv)
|
||||||
char *filenames[FD_NUMS] = { NULL, };
|
char *filenames[FD_NUMS] = { NULL, };
|
||||||
int n_read;
|
int n_read;
|
||||||
int r, opt;
|
int r, opt;
|
||||||
file_ctx_t f;
|
file_ctx_t f = { 0 };
|
||||||
int dev_index = 0;
|
int dev_index = 0;
|
||||||
unsigned int num_bufs = 0;
|
unsigned int num_bufs = 0;
|
||||||
bool fname0_used = false;
|
bool fname0_used = false;
|
||||||
bool have_file = false;
|
bool have_file = false;
|
||||||
|
|
||||||
while ((opt = getopt(argc, argv, "0:1:2:3:d:b:")) != -1) {
|
while ((opt = getopt(argc, argv, "0:1:2:3:d:b:l:t:")) != -1) {
|
||||||
switch (opt) {
|
switch (opt) {
|
||||||
case 'd':
|
case 'd':
|
||||||
dev_index = (uint32_t)atoi(optarg);
|
dev_index = (uint32_t)atoi(optarg);
|
||||||
|
|
@ -130,6 +240,12 @@ int main(int argc, char **argv)
|
||||||
case 'b':
|
case 'b':
|
||||||
num_bufs = (unsigned int)atoi(optarg);
|
num_bufs = (unsigned int)atoi(optarg);
|
||||||
break;
|
break;
|
||||||
|
case 'l':
|
||||||
|
flac_level = atoi(optarg);
|
||||||
|
break;
|
||||||
|
case 't':
|
||||||
|
flac_nthreads = atoi(optarg);
|
||||||
|
break;
|
||||||
case '0':
|
case '0':
|
||||||
fname0_used = true;
|
fname0_used = true;
|
||||||
have_file = true;
|
have_file = true;
|
||||||
|
|
@ -184,6 +300,7 @@ int main(int argc, char **argv)
|
||||||
|
|
||||||
for (int i = 0; i < FD_NUMS; i++) {
|
for (int i = 0; i < FD_NUMS; i++) {
|
||||||
f.files[i] = NULL;
|
f.files[i] = NULL;
|
||||||
|
f.encoder[i] = NULL;
|
||||||
|
|
||||||
if (!filenames[i])
|
if (!filenames[i])
|
||||||
continue;
|
continue;
|
||||||
|
|
@ -199,24 +316,36 @@ int main(int argc, char **argv)
|
||||||
fprintf(stderr, "Failed to open %s\n", filenames[i]);
|
fprintf(stderr, "Failed to open %s\n", filenames[i]);
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
char *dot = strrchr(filenames[i], '.');
|
||||||
|
if (dot && !strcmp(dot, ".flac"))
|
||||||
|
f.use_flac[i] = true;
|
||||||
|
else
|
||||||
|
f.use_flac[i] = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
r = hsdaoh_start_stream(dev, hsdaoh_callback, (void *)&f, num_bufs);
|
r = hsdaoh_start_stream(dev, hsdaoh_callback, (void *)&f, num_bufs);
|
||||||
|
|
||||||
while (!do_exit) {
|
while (!do_exit)
|
||||||
usleep(50000);
|
usleep(50000);
|
||||||
}
|
|
||||||
|
|
||||||
if (do_exit)
|
|
||||||
fprintf(stderr, "\nUser cancel, exiting...\n");
|
fprintf(stderr, "\nUser cancel, exiting...\n");
|
||||||
else
|
|
||||||
fprintf(stderr, "\nLibrary error %d, exiting...\n", r);
|
|
||||||
|
|
||||||
hsdaoh_close(dev);
|
hsdaoh_close(dev);
|
||||||
|
|
||||||
for (int i = 0; i < FD_NUMS; i++) {
|
for (int i = 0; i < FD_NUMS; i++) {
|
||||||
if (f.files[i] && (f.files[i] != stdout))
|
if (!f.files[i])
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (f.use_flac[i] && f.encoder[i]) {
|
||||||
|
FLAC__bool ret = FLAC__stream_encoder_finish(f.encoder[i]);
|
||||||
|
|
||||||
|
if (!ret)
|
||||||
|
fprintf(stderr, "ERROR: FLAC encoder did not finish correctly: %s\n",
|
||||||
|
FLAC__StreamEncoderStateString[FLAC__stream_encoder_get_state(f.encoder[i])]);
|
||||||
|
|
||||||
|
FLAC__stream_encoder_delete(f.encoder[i]);
|
||||||
|
} else if (f.files[i] != stdout)
|
||||||
fclose(f.files[i]);
|
fclose(f.files[i]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -52,7 +52,7 @@
|
||||||
#include <format_convert.h>
|
#include <format_convert.h>
|
||||||
#include <crc.h>
|
#include <crc.h>
|
||||||
|
|
||||||
#define DEFAULT_BUFFERS 16
|
#define DEFAULT_BUFFERS 96
|
||||||
|
|
||||||
typedef struct hsdaoh_adapter {
|
typedef struct hsdaoh_adapter {
|
||||||
uint16_t vid;
|
uint16_t vid;
|
||||||
|
|
@ -531,8 +531,6 @@ int hsdaoh_close(hsdaoh_dev_t *dev)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
// maybe rename to preferred output format
|
|
||||||
// and add real output format to data_info_t
|
|
||||||
int hsdaoh_set_output_format(hsdaoh_dev_t *dev, hsdaoh_output_format_t format)
|
int hsdaoh_set_output_format(hsdaoh_dev_t *dev, hsdaoh_output_format_t format)
|
||||||
{
|
{
|
||||||
if (!dev)
|
if (!dev)
|
||||||
|
|
@ -542,13 +540,14 @@ int hsdaoh_set_output_format(hsdaoh_dev_t *dev, hsdaoh_output_format_t format)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void hsdaoh_output(hsdaoh_dev_t *dev, uint16_t sid, int format, uint8_t *data, size_t len)
|
void hsdaoh_output(hsdaoh_dev_t *dev, uint16_t sid, int format, uint32_t srate, uint8_t *data, size_t len)
|
||||||
{
|
{
|
||||||
hsdaoh_data_info_t data_info;
|
hsdaoh_data_info_t data_info;
|
||||||
data_info.ctx = dev->cb_ctx;
|
data_info.ctx = dev->cb_ctx;
|
||||||
data_info.stream_id = sid;
|
data_info.stream_id = sid;
|
||||||
data_info.buf = data;
|
data_info.buf = data;
|
||||||
data_info.len = len;
|
data_info.len = len;
|
||||||
|
data_info.srate = srate;
|
||||||
|
|
||||||
switch (format) {
|
switch (format) {
|
||||||
case PIO_8BIT_IQ:
|
case PIO_8BIT_IQ:
|
||||||
|
|
@ -604,7 +603,7 @@ static void *hsdaoh_output_worker(void *arg)
|
||||||
pthread_mutex_unlock(&dev->ll_mutex);
|
pthread_mutex_unlock(&dev->ll_mutex);
|
||||||
|
|
||||||
while (curelem != NULL) {
|
while (curelem != NULL) {
|
||||||
hsdaoh_output(dev, curelem->sid, curelem->format, curelem->data, curelem->len);
|
hsdaoh_output(dev, curelem->sid, curelem->format, curelem->srate, curelem->data, curelem->len);
|
||||||
|
|
||||||
prev = curelem;
|
prev = curelem;
|
||||||
curelem = curelem->next;
|
curelem = curelem->next;
|
||||||
|
|
@ -614,7 +613,7 @@ static void *hsdaoh_output_worker(void *arg)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void hsdaoh_enqueue_data(hsdaoh_dev_t *dev, uint16_t sid, int format, uint8_t *data, size_t len)
|
void hsdaoh_enqueue_data(hsdaoh_dev_t *dev, uint16_t sid, int format, uint32_t srate, uint8_t *data, size_t len)
|
||||||
{
|
{
|
||||||
if (dev->async_status != HSDAOH_RUNNING) {
|
if (dev->async_status != HSDAOH_RUNNING) {
|
||||||
free(data);
|
free(data);
|
||||||
|
|
@ -626,6 +625,7 @@ void hsdaoh_enqueue_data(hsdaoh_dev_t *dev, uint16_t sid, int format, uint8_t *d
|
||||||
rpt->len = len;
|
rpt->len = len;
|
||||||
rpt->sid = sid;
|
rpt->sid = sid;
|
||||||
rpt->format = format;
|
rpt->format = format;
|
||||||
|
rpt->srate = srate;
|
||||||
rpt->next = NULL;
|
rpt->next = NULL;
|
||||||
|
|
||||||
pthread_mutex_lock(&dev->ll_mutex);
|
pthread_mutex_lock(&dev->ll_mutex);
|
||||||
|
|
@ -721,6 +721,11 @@ void hsdaoh_process_frame(hsdaoh_dev_t *dev, uint8_t *data, int size)
|
||||||
uint16_t stream0_format = 0;
|
uint16_t stream0_format = 0;
|
||||||
uint8_t *stream0_data = malloc(dev->width-1 * dev->height * 2);
|
uint8_t *stream0_data = malloc(dev->width-1 * dev->height * 2);
|
||||||
|
|
||||||
|
if (!stream0_data) {
|
||||||
|
fprintf(stderr, "Out of memory, frame skipped!\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
for (unsigned int i = 0; i < dev->height; i++) {
|
for (unsigned int i = 0; i < dev->height; i++) {
|
||||||
uint8_t *line_dat = data + (dev->width * sizeof(uint16_t) * i);
|
uint8_t *line_dat = data + (dev->width * sizeof(uint16_t) * i);
|
||||||
|
|
||||||
|
|
@ -745,6 +750,7 @@ void hsdaoh_process_frame(hsdaoh_dev_t *dev, uint8_t *data, int size)
|
||||||
fprintf(stderr, "Invalid payload length: %d\n", payload_len);
|
fprintf(stderr, "Invalid payload length: %d\n", payload_len);
|
||||||
|
|
||||||
/* discard frame */
|
/* discard frame */
|
||||||
|
free(stream0_data);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -770,14 +776,15 @@ void hsdaoh_process_frame(hsdaoh_dev_t *dev, uint8_t *data, int size)
|
||||||
stream0_format = format;
|
stream0_format = format;
|
||||||
} else {
|
} else {
|
||||||
uint8_t *out_data = malloc(out_len);
|
uint8_t *out_data = malloc(out_len);
|
||||||
|
uint32_t srate = stream_id < DEFAULT_MAX_STREAMS ? meta.stream_info[stream_id].srate : 0;
|
||||||
memcpy(out_data, line_dat, out_len);
|
memcpy(out_data, line_dat, out_len);
|
||||||
hsdaoh_enqueue_data(dev, stream_id, format, out_data, out_len);
|
hsdaoh_enqueue_data(dev, stream_id, format, srate, out_data, out_len);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (dev->stream_synced && stream0_payload_bytes)
|
if (dev->stream_synced && stream0_payload_bytes)
|
||||||
hsdaoh_enqueue_data(dev, 0, stream0_format, stream0_data, stream0_payload_bytes);
|
hsdaoh_enqueue_data(dev, 0, stream0_format, meta.stream_info[0].srate, stream0_data, stream0_payload_bytes);
|
||||||
else
|
else
|
||||||
free(stream0_data);
|
free(stream0_data);
|
||||||
|
|
||||||
|
|
@ -788,7 +795,7 @@ void hsdaoh_process_frame(hsdaoh_dev_t *dev, uint8_t *data, int size)
|
||||||
dev->frames_since_error++;
|
dev->frames_since_error++;
|
||||||
|
|
||||||
if (!dev->stream_synced && !frame_errors && (dev->in_order_cnt > 4)) {
|
if (!dev->stream_synced && !frame_errors && (dev->in_order_cnt > 4)) {
|
||||||
fprintf(stderr, "Syncronized to HDMI input stream\n");
|
fprintf(stderr, "Synchronized to HDMI input stream\n");
|
||||||
dev->stream_synced = true;
|
dev->stream_synced = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -839,7 +846,7 @@ int hsdaoh_start_stream(hsdaoh_dev_t *dev, hsdaoh_read_cb_t cb, void *ctx, unsig
|
||||||
// dev->output_float = true;
|
// dev->output_float = true;
|
||||||
|
|
||||||
/* initialize with a threshold */
|
/* initialize with a threshold */
|
||||||
dev->highest_numq = 9;
|
dev->highest_numq = DEFAULT_BUFFERS/2;
|
||||||
dev->llbuf_num = (buf_num == 0) ? DEFAULT_BUFFERS : buf_num;
|
dev->llbuf_num = (buf_num == 0) ? DEFAULT_BUFFERS : buf_num;
|
||||||
|
|
||||||
pthread_mutex_init(&dev->ll_mutex, NULL);
|
pthread_mutex_init(&dev->ll_mutex, NULL);
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue