CIN_DSP_CONVERT_SAMPLE_INT_TO_INT(COMMON_TYPE, OUT_TYPE, X)
#endif
-static void CIN_DSP_CONVERT_NAME (
+/* Used in the stereo to mono specialization. */
+#define CIN_DSP_STEREO_TO_MONO(IN_PTR, COMMON_VALUE, DEST_PTR) do{ \
+ const IN_TYPE *const STM_in_ptr = (IN_PTR); \
+ const IN_TYPE STM_in_sample0 = in[0]; \
+ const IN_TYPE STM_in_sample1 = in[1]; \
+ const COMMON_TYPE STM_common_sample0 = \
+ CIN_DSP_CONVERT_SAMPLE_IN_TO_COMMON(STM_in_sample0); \
+ const COMMON_TYPE STM_common_sample1 = \
+ CIN_DSP_CONVERT_SAMPLE_IN_TO_COMMON(STM_in_sample1); \
+ const COMMON_TYPE STM_common_value = (COMMON_VALUE) = \
+ (STM_common_sample0 / (COMMON_TYPE)2) + \
+ (STM_common_sample1 / (COMMON_TYPE)2); \
+ (void)sizeof((COMMON_VALUE)); \
+ (DEST_PTR)[0] = \
+ CIN_DSP_CONVERT_SAMPLE_COMMON_TO_OUT(STM_common_value); \
+ } while(0)
+
+
+static unsigned CIN_DSP_CONVERT_NAME (
const unsigned num_in_frames,
const IN_TYPE *in,
OUT_TYPE *out,
const ldiv_t rate = ldiv(out_sample_rate, in_sample_rate);
int at = 0;
unsigned i, channel;
+ /* Used for the return value */
+ const unsigned char *const original_out = (unsigned char*)out;
+
+ unsigned out_frames = 0;
for(i = 0; i < num_in_frames; i++){
- unsigned out_frames = 0;
+ out_frames = 0;
at -= rate.rem;
if(at < 0){
at += in_sample_rate;
out_frames += rate.quot;
if(out_frames != 0){
- {
+ /* Special case for stereo -> mono. Ideally this would NOT be
+ * checked inside the loop :(
+ */
+ if(in_num_channels == 2 && out_num_channels == 1){
+ COMMON_TYPE unused;
+ CIN_DSP_STEREO_TO_MONO(in, unused, out);
+ }
+ else{
/* Hold on to the final channel we processed, so that we can
* paste it over all the remaining channels.
*/
in += in_num_channels;
out_frames--;
- if(out_frames != 0){
+
+ if(out_frames != 0 && (i + 1) < num_in_frames){
COMMON_TYPE common_step, common_sample_begin;
+
+ /* Special case for stereo -> mono. Ideally this would NOT be
+ * checked inside the loop :(
+ */
+ if(in_num_channels == 2 && out_num_channels == 1){
+ unsigned frame = 0;
+ /* Get the input samples we will lerp between. */
+ COMMON_TYPE common_sample, common_sample_end;
+ OUT_TYPE unused;
+ CIN_DSP_STEREO_TO_MONO(in+0, common_sample, &unused);
+ CIN_DSP_STEREO_TO_MONO(in+2, common_sample_end, &unused);
+ /* This is slightly lossy with integer common types and
+ * small steps. */
+ common_step =
+ (common_sample_end - common_sample) /
+ ((COMMON_TYPE)out_frames);
+
+ while(1){
+ out[0] = common_sample;
+ if(++frame == out_frames)
+ break;
+ common_sample += common_step;
+ out++;
+ }
+
+ /* This skips the below loops in a way that plays well with
+ * GCC, clang, and MSVC's optimizers.
+ */
+ channel = out_num_channels;
+ }
+ else{
+ channel = 0;
+ }
+
/* Process per-channel, since we need to do lerps. */
- for(channel = 0; channel < num_common_channels; channel++){
+ for(;channel < num_common_channels; channel++){
/* Get the input samples we will lerp between. */
const IN_TYPE in_sample_start = in[channel];
const IN_TYPE in_sample_end =
const COMMON_TYPE common_sample_end =
CIN_DSP_CONVERT_SAMPLE_IN_TO_COMMON(in_sample_end);
- OUT_TYPE *dest = out + channel; /* Will be advanced on each frame */
+ /* Will be advanced on each frame */
+ OUT_TYPE *dest = out + channel;
unsigned frame = 0;
/* This is slightly lossy with integer common types and
}
}
- /* Repeat just the stepping for any remaining channels. */
+ /* Copy the last written channel over all remaining channels.
+ */
for(; channel < out_num_channels; channel++){
- OUT_TYPE *dest = out + channel; /* Will be advanced on each frame */
+ /* Will be advanced on each frame */
+ OUT_TYPE *dest = out + channel;
COMMON_TYPE common_sample = common_sample_begin;
unsigned frame = 0;
while(1){
}
in += in_num_channels;
- out += out_num_channels * (out_frames + 1);
+ out += out_num_channels * out_frames;
}
}
}
+
+ return ((unsigned char*)out) - original_out;
}
+#undef CIN_DSP_STEREO_TO_MONO
#undef COMMON_TYPE
#undef OUT_TYPE
#undef IN_TYPE
\
switch(num_streams){ \
case 0: return; \
- case 1: memmove(out, *in, num_bytes); return; \
+ case 1: \
+ if(*in != out) \
+ memmove(out, *in, num_bytes); \
+ return; \
default: \
case 4: data[3] = in[3]; \
case 3: data[2] = in[2]; \
void *out_data){
#define CIN_DSP_CONVERT_CALL(IN_TYPE, OUT_TYPE) \
- (num_bytes / sizeof(IN_TYPE))/in_format->num_channels, \
+ (num_bytes / (sizeof(IN_TYPE) * in_format->num_channels)), \
(const IN_TYPE *)in_data, \
(OUT_TYPE *)out_data, \
in_format->sample_rate, \
#define CIN_DSP_CONVERT_1(IN_TYPE) \
switch(out_format->sample_format){ \
case CIN_DSP_FORMAT_S8: \
- Cin_DSP_Convert ## IN_TYPE ## S8( \
- CIN_DSP_CONVERT_CALL(IN_TYPE, S8) ); break; \
+ return Cin_DSP_Convert ## IN_TYPE ## S8( \
+ CIN_DSP_CONVERT_CALL(IN_TYPE, S8) ); \
case CIN_DSP_FORMAT_S16: \
- Cin_DSP_Convert ## IN_TYPE ## S16( \
- CIN_DSP_CONVERT_CALL(IN_TYPE, S16) ); break; \
+ return Cin_DSP_Convert ## IN_TYPE ## S16( \
+ CIN_DSP_CONVERT_CALL(IN_TYPE, S16) ); \
case CIN_DSP_FORMAT_S32: \
- Cin_DSP_Convert ## IN_TYPE ## S32( \
- CIN_DSP_CONVERT_CALL(IN_TYPE, S32) ); break; \
+ return Cin_DSP_Convert ## IN_TYPE ## S32( \
+ CIN_DSP_CONVERT_CALL(IN_TYPE, S32) ); \
case CIN_DSP_FORMAT_FLOAT32: \
- Cin_DSP_Convert ## IN_TYPE ## Float32( \
- CIN_DSP_CONVERT_CALL(IN_TYPE, Float32) ); break; \
+ return Cin_DSP_Convert ## IN_TYPE ## Float32( \
+ CIN_DSP_CONVERT_CALL(IN_TYPE, Float32) ); \
case CIN_DSP_FORMAT_FLOAT64: \
- Cin_DSP_Convert ## IN_TYPE ## Float64( \
- CIN_DSP_CONVERT_CALL(IN_TYPE, Float64) ); break; \
+ return Cin_DSP_Convert ## IN_TYPE ## Float64( \
+ CIN_DSP_CONVERT_CALL(IN_TYPE, Float64) ); \
default: \
return 0; \
}
switch(in_format->sample_format){
case CIN_DSP_FORMAT_S8:
- CIN_DSP_CONVERT_1(S8); break;
+ CIN_DSP_CONVERT_1(S8);
case CIN_DSP_FORMAT_S16:
- CIN_DSP_CONVERT_1(S16); break;
+ CIN_DSP_CONVERT_1(S16);
case CIN_DSP_FORMAT_S32:
- CIN_DSP_CONVERT_1(S32); break;
+ CIN_DSP_CONVERT_1(S32);
case CIN_DSP_FORMAT_FLOAT32:
- CIN_DSP_CONVERT_1(Float32); break;
+ CIN_DSP_CONVERT_1(Float32);
case CIN_DSP_FORMAT_FLOAT64:
- CIN_DSP_CONVERT_1(Float64); break;
+ CIN_DSP_CONVERT_1(Float64);
default:
return 0;
* @param in_data Input data to convert
* @param out_format Format to convert to
* @param out_data Destination buffer.
- * @return Number of bytes converted.
+ * @return Number of bytes converted, or 0 on error.
*
* @sa Cin_DSP_ConversionSize
*/
/**
* @brief Mixes multiple audio buffers of the same format and size.
*
+ * Mixing can re-use an input buffer, although if out_data appears in the
+ * in_data array it MUST be the first element and must only appear once.
+ *
* @param num_bytes Number of bytes of data to consume
* @param format Format of data to consume
* @param num_streams Number of streams in @p in_data
* You must include cin_soft_loader.h and cin_dsp.h to use this.
* This is only a macro to allow us to avoid adding an additional library just
* to glue together SoftLoader and MixerSound.
+ * TODO: This just swallows errors!
*/
#define CIN_MIXER_SOUND_FROM_SOFT_LOADER(LD, FMT, OUT, ALLOCATOR) do{ \
const struct Cin_Loader *const CIN_MIXER_loader = (LD);\
struct Cin_MixerSound *const CIN_MIXER_out = \
ALLOCATOR(sizeof(struct Cin_MixerSound) + CIN_MIXER_out_size); \
(OUT) = CIN_MIXER_out; \
+ CIN_MIXER_out->byte_len = CIN_MIXER_out_size; \
for(CIN_MIXER_loader_data = CIN_MIXER_loader->first; \
CIN_MIXER_loader_data != NULL; \
CIN_MIXER_loader_data = CIN_MIXER_loader_data->next){ \
CIN_SOFT_LOADER_DATA(CIN_MIXER_loader_data), \
CIN_MIXER_out_format, \
CIN_MIXER_SOUND_PCM(CIN_MIXER_out) + CIN_MIXER_i); \
+ if(CIN_MIXER_added == 0) break; \
CIN_MIXER_i += CIN_MIXER_added; \
} \
} \
cin_mixer_sound.o: cin_mixer_sound.c cin_mixer_sound.h cin_dsp.h $(ROOTDIR)/cin_export.h $(ROOTDIR)/cin_format.h
$(CC) $(CFLAGS) -I"$(ROOTDIR)" -c cin_mixer_sound.c -o cin_mixer_sound.o
-cin_dsp.o: cin_dsp.c cin_dsp.h
+cin_dsp.o: cin_dsp.c cin_dsp.h cin_convert_core.inc
$(CC) $(CFLAGS) -c cin_dsp.c -o cin_dsp.o
#include <assert.h>
#include <stdlib.h>
+#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <time.h>
const int bytes_per_frame = CIN_FORMAT_BYTES_PER_SAMPLE(format) * num_channels;
const int buffer_size = (bytes_per_frame * rate) / 10;
+ const void *input_data[CIN_OSS_SOUND_CHANNELS+1];
+
void *const buffer = malloc(buffer_size);
CIN_DSP_NEW_STACK_FMT_STRUCT(dsp_fmt);
}
if(active_channels != 0){
- Cin_DSP_Mix(buffer_size, dsp_fmt, active_channels, channels, buffer);
+ /* First, mix any finalizing channels (channels without enough data to
+ * fill the buffer)
+ */
i = 0;
+
+ /* Find the first channel which is being finalized, if any. */
do{
- channels[i]->position += buffer_size;
+ if(channels[i]->position + buffer_size >
+ channels[i]->byte_len){
+ break;
+ }
}while(++i < active_channels);
+
+ /* Check this only if we broke early (found a finalizing channel) */
+ if(i != active_channels){
+ input_data[0] = buffer;
+
+ /* Very fortunately, zero-init is ALSO zero in float and double */
+ memset(buffer, 0, buffer_size);
+
+ do{
+ struct Cin_MixerSound *const snd = channels[i];
+ const int remaining_length =
+ snd->byte_len - snd->position;
+ if(remaining_length < buffer_size){
+ input_data[1] = CIN_MIXER_SOUND_PCM(channels[i]) +
+ snd->position;
+
+ Cin_DSP_Mix(remaining_length,
+ dsp_fmt,
+ 2,
+ input_data,
+ buffer);
+
+ /* Pull out this channel by simply swapping in the final
+ * element. This makes removing any element constant time,
+ * and is acceptable because the order of the channels is
+ * not important.
+ */
+ channels[i] = channels[--active_channels];
+ }
+ else{
+ i++;
+ }
+ }while(i < active_channels);
+
+ /* Set i = 1, so that we do not place anything over the input_data
+ * element containing our mixed finalizing sounds.
+ */
+ i = 1;
+ }
+ else{
+ /* No finalizing sounds, so no extra inputs. */
+ i = 0;
+ }
+
+ /* No need to remix if all channels were finalizing. */
+ if(active_channels != 0){
+ register unsigned source_i = 0;
+ do{
+ input_data[i++] = CIN_MIXER_SOUND_PCM(channels[source_i]) +
+ channels[source_i]->position;
+ channels[source_i]->position += buffer_size;
+ }while(++source_i < active_channels);
+ }
+ /* i contains the final element placed into input_data, regardless
+ * of how many were from the input channels.
+ */
+ Cin_DSP_Mix(buffer_size, dsp_fmt, i, input_data, buffer);
}
else{
memset(buffer, 0, buffer_size);
#include "cin_oss_driver.h"
#include "cin_oss_sound.h"
+#include "cin_mixer_sound.h"
#include "cin_oss.h"
#include "cinnamon.h"
CIN_PRIVATE(int) Cin_OSS_PushCommand(struct Cin_Driver *drv,
struct Cin_OSS_Command *cmd){
TAILQ_INSERT_TAIL(&drv->commands, cmd, entries);
- return 1;
+ return 0;
}
/*****************************************************************************/
switch(command){
case CIN_OSS_INSERT:
- puts("CIN_OSS_INSERT");
+ /* puts("CIN_OSS_INSERT"); */
SLIST_INSERT_HEAD(&drv->sounds, sound, entries);
break;
case CIN_OSS_DELETE:
- puts("CIN_OSS_DELETE");
+ /* puts("CIN_OSS_DELETE"); */
if(prev == NULL)
SLIST_REMOVE_HEAD(&drv->sounds, entries);
else
+ /* When compiling on Linux with OSS emulation, we need to
+ * deal with not having this macro. */
+#ifndef SLIST_REMOVE_AFTER
+ SLIST_REMOVE(&drv->sounds,
+ SLIST_NEXT(prev, entries),
+ Cin_OSS_Sound,
+ entries);
+#else
SLIST_REMOVE_AFTER(prev, entries);
+#endif
/* We want to stop any playing instances of this sound. */
/* FALLTHROUGH */
case CIN_OSS_STOP:
- if(command == CIN_OSS_STOP)
- puts("CIN_OSS_STOP");
+ /* if(command == CIN_OSS_STOP)
+ puts("CIN_OSS_STOP"); */
for(i = 0; i < CIN_OSS_SOUND_CHANNELS; i++){
if(drv->channels[i] == sound->snd){
drv->channels[i] = NULL;
}
break;
case CIN_OSS_PLAY:
- puts("CIN_OSS_PLAY");
+ /* puts("CIN_OSS_PLAY"); */
+
+ /* Check if the sound is already playing, and do nothing
+ * if it is.
+ */
+ for(i = 0; i < CIN_OSS_SOUND_CHANNELS; i++){
+ if(drv->channels[i] == sound->snd){
+ sound = NULL;
+ /* Found. */
+ break;
+ }
+ }
+ if(sound == NULL)
+ break;
+
for(i = 0; i < CIN_OSS_SOUND_CHANNELS; i++){
if(drv->channels[i] == NULL){
drv->channels[i] = sound->snd;
+ drv->channels[i]->position = 0;
break;
}
}
*/
break;
case CIN_OSS_QUIT:
- puts("CIN_OSS_QUIT");
+ /* puts("CIN_OSS_QUIT"); */
/* Keep going so that we at least drain the queue. */
val = 1;
break;
drv->rate = val;
- /* Set the buffer size. */
+ /* Set the buffer size.
val = 0x0004000B;
if(ioctl(dev, SNDCTL_DSP_SETFRAGMENT, &val) == -1){
ret = Cin_eDriverFailure;
goto fail_device;
- }
+ } */
}
{
Cin_OSS_PushCommand(drv, quit);
{
void *unused;
- pthread_join(&drv->thread, &unused);
+ pthread_join(drv->thread, &unused);
}
/* Finalize the driver. */
close(drv->dev);
struct Cin_OSS_Command *cmd = Cin_OSS_NewCommand(snd->sound_id, type);
if(Cin_OSS_Lock(snd->drv) != 0){
+ assert(!"Could not lock");
return Cin_eSoundFailure;
}
- if(Cin_OSS_PushCommand(snd->drv, cmd) != 0)
+ if(Cin_OSS_PushCommand(snd->drv, cmd) != 0){
+ assert(!"Could not push command");
ret = Cin_eSoundFailure;
-
- if(Cin_OSS_Unlock(snd->drv) != 0)
+ }
+
+ if(Cin_OSS_Unlock(snd->drv) != 0){
+ assert(!"Could not unlock");
return Cin_eSoundFailure;
- else
+ }
+ else{
return ret;
+ }
}
/*****************************************************************************/
CIN_MIXERLIB=$(ROOTDIR)/common/libcin_mixer.a
libcin_oss_x.a: $(OBJECTS)
- if [[ -f libcin_oss_x2.a ]] ; then rm libcin_oss_x2.a ; fi
+ if [ -f libcin_oss_x2.a ] ; then rm libcin_oss_x2.a ; fi
$(AR) rc libcin_oss_x2.a $(OBJECTS)
$(RANLIB) libcin_oss_x2.a
mv libcin_oss_x2.a libcin_oss_x.a
libcin_oss.a: $(CIN_COMMONLIB) $(CIN_MIXERLIB) libcin_oss_x.a
- if [[ -f libcin_oss2.a ]] ; then rm libcin_oss2.a ; fi
- echo $$'create libcin_oss2.a\naddlib libcin_oss_x.a\naddlib $(CIN_COMMONLIB)\naddlib $(CIN_MIXERLIB)\nsave\nend\n' | $(AR) M
+ if [ -f libcin_oss2.a ] ; then rm libcin_oss2.a ; fi
+ echo 'create libcin_oss2.a\naddlib libcin_oss_x.a\naddlib $(CIN_COMMONLIB)\naddlib $(CIN_MIXERLIB)\nsave\nend\n' | $(AR) M
$(RANLIB) libcin_oss2.a
mv libcin_oss2.a libcin_oss.a
$(CC) $(CFLAGS) -I"$(ROOTDIR)" -I"$(ROOTDIR)/common" -c cin_oss.c -o cin_oss.o
cin_oss_driver.o: cin_oss_driver.c cin_oss_driver.h cin_oss.h $(COMMONHEADERS)
- $(CC) $(CFLAGS) -I"$(ROOTDIR)" -c cin_oss_driver.c -o cin_oss_driver.o
+ $(CC) $(CFLAGS) -I"$(ROOTDIR)" -I"$(ROOTDIR)/common" -c cin_oss_driver.c -o cin_oss_driver.o
cin_oss_sound.o: cin_oss_sound.c cin_oss.h $(COMMONHEADERS) ../common/cin_soft_loader.h ../common/cin_mixer_sound.h
$(CC) $(CFLAGS) -I"$(ROOTDIR)" -I"$(ROOTDIR)/common" -c cin_oss_sound.c -o cin_oss_sound.o