aboutsummaryrefslogtreecommitdiff
path: root/Src/replicant/audio
diff options
context:
space:
mode:
Diffstat (limited to 'Src/replicant/audio')
-rw-r--r--Src/replicant/audio/ifc_audio_decoder_callback.h57
-rw-r--r--Src/replicant/audio/ifc_audio_decoder_packet.h23
-rw-r--r--Src/replicant/audio/ifc_audio_decoder_pull.h34
-rw-r--r--Src/replicant/audio/ifc_audioout.h76
-rw-r--r--Src/replicant/audio/ifc_equalizer.h27
-rw-r--r--Src/replicant/audio/parameters.h47
-rw-r--r--Src/replicant/audio/types.h44
7 files changed, 308 insertions, 0 deletions
diff --git a/Src/replicant/audio/ifc_audio_decoder_callback.h b/Src/replicant/audio/ifc_audio_decoder_callback.h
new file mode 100644
index 00000000..2db3fa1d
--- /dev/null
+++ b/Src/replicant/audio/ifc_audio_decoder_callback.h
@@ -0,0 +1,57 @@
+#pragma once
+#include "foundation/dispatch.h"
+#include "nx/nxuri.h"
+#include "metadata/ifc_metadata.h"
+
+/* this is the class you actually use */
+class ifc_audio_decoder_callback : public Wasabi2::Dispatchable
+{
+protected:
+ ifc_audio_decoder_callback() : Dispatchable(DISPATCHABLE_VERSION) {}
+ ~ifc_audio_decoder_callback() {}
+public:
+ /* you must implement this class to use the decoder */
+ class callback : public Wasabi2::Dispatchable
+ {
+ protected:
+ callback() : Dispatchable(DISPATCHABLE_VERSION) {}
+ ~callback() {}
+ public:
+ /* frames is defined as all channels, e.g. 16bit stereo is 4 bytes per frame (2 bytes per sample)
+ return NErr_Success to continue receiving callbacks
+ */
+ int OnAudio(const void *buffer, size_t buffer_frames) { return AudioDecoderCallback_OnAudio(buffer, buffer_frames); }
+
+ enum
+ {
+ DISPATCHABLE_VERSION=0,
+ };
+ private:
+ virtual int WASABICALL AudioDecoderCallback_OnAudio(const void *buffer, size_t buffer_frames)=0;
+ };
+
+ /* if possible, returns an upper bound on the number of frames used internally. this would be the maximum buffer_frames value you receive in a callback */
+ int GetFrameSize(size_t *frame_size) { return AudioDecoderCallback_GetFrameSize(frame_size); }
+ int GetMetadata(ifc_metadata **metadata) { return AudioDecoderCallback_GetMetadata(metadata); }
+
+ /* returns
+ * NErr_Success on a successfully completed decode
+ * NErr_Interrupted if the callback function aborted decoding
+ * anything else indicates a decoding error
+ */
+ int Decode(ifc_audio_decoder_callback::callback *callback) { return AudioDecoderCallback_Decode(callback); }
+
+ /* Like decode, but only processes one frame.
+ returns NErr_EndOfFile on the last frame */
+ int DecodeStep(ifc_audio_decoder_callback::callback *callback) { return AudioDecoderCallback_DecodeStep(callback); }
+
+ enum
+ {
+ DISPATCHABLE_VERSION=0,
+ };
+private:
+ virtual int WASABICALL AudioDecoderCallback_Decode(ifc_audio_decoder_callback::callback *callback)=0;
+ virtual int WASABICALL AudioDecoderCallback_DecodeStep(ifc_audio_decoder_callback::callback *callback)=0;
+ virtual int WASABICALL AudioDecoderCallback_GetFrameSize(size_t *frame_size)=0;
+ virtual int WASABICALL AudioDecoderCallback_GetMetadata(ifc_metadata **metadata)=0;
+};
diff --git a/Src/replicant/audio/ifc_audio_decoder_packet.h b/Src/replicant/audio/ifc_audio_decoder_packet.h
new file mode 100644
index 00000000..cd929899
--- /dev/null
+++ b/Src/replicant/audio/ifc_audio_decoder_packet.h
@@ -0,0 +1,23 @@
+#pragma once
+#include "foundation/dispatch.h"
+#include "nx/nxuri.h"
+#include "metadata/ifc_metadata.h"
+
+class ifc_audio_decoder_packet : public Wasabi2::Dispatchable
+{
+protected:
+ ifc_audio_decoder_packet() : Dispatchable(DISPATCHABLE_VERSION) {}
+ ~ifc_audio_decoder_packet() {}
+public:
+
+ int GetMetadata(ifc_metadata **metadata) { return AudioDecoderPacket_GetMetadata(metadata); }
+ int Decode(void **out_packet, size_t *frames_available) { return AudioDecoderPacket_Decode(out_packet, frames_available); }
+
+ enum
+ {
+ DISPATCHABLE_VERSION=0,
+ };
+private:
+ virtual int WASABICALL AudioDecoderPacket_GetMetadata(ifc_metadata **metadata)=0;
+ virtual int WASABICALL AudioDecoderPacket_Decode(void **out_packet, size_t *frames_available)=0;
+};
diff --git a/Src/replicant/audio/ifc_audio_decoder_pull.h b/Src/replicant/audio/ifc_audio_decoder_pull.h
new file mode 100644
index 00000000..9be97987
--- /dev/null
+++ b/Src/replicant/audio/ifc_audio_decoder_pull.h
@@ -0,0 +1,34 @@
+#pragma once
+#include "foundation/dispatch.h"
+#include "nx/nxuri.h"
+#include "metadata/ifc_metadata.h"
+
+class ifc_audio_decoder_pull : public Wasabi2::Dispatchable
+{
+protected:
+ ifc_audio_decoder_pull() : Dispatchable(DISPATCHABLE_VERSION) {}
+ ~ifc_audio_decoder_pull() {}
+public:
+
+ /* if possible, returns an upper bound on the number of frames used internally. pull decoders are most optimal if you use this to malloc your buffer */
+ int GetFrameSize(size_t *frame_size) { return AudioDecoderPull_GetFrameSize(frame_size); }
+ int GetMetadata(ifc_metadata **metadata) { return AudioDecoderPull_GetMetadata(metadata); }
+
+ /* returns
+ * NErr_EndOfFile when decode is done (frames_written will be valid, but probably 0)
+ * NErr_Success on successful decode, but not end-of-file (frames_written will be valid)
+ * anything else indicates a decode error */
+ int Decode(void *buffer, size_t buffer_frames, size_t *frames_written) { return AudioDecoderPull_Decode(buffer, buffer_frames, frames_written); }
+ /* You need to call Close() when you are done (even if you Release) because some implementations might have ifc_metadata being the same object */
+ void Close() { AudioDecoderPull_Close(); }
+
+ enum
+ {
+ DISPATCHABLE_VERSION=0,
+ };
+private:
+ virtual int WASABICALL AudioDecoderPull_GetFrameSize(size_t *frame_size)=0;
+ virtual int WASABICALL AudioDecoderPull_GetMetadata(ifc_metadata **metadata)=0;
+ virtual int WASABICALL AudioDecoderPull_Decode(void *buffer, size_t buffer_frames, size_t *frames_written)=0;
+ virtual void WASABICALL AudioDecoderPull_Close()=0;
+};
diff --git a/Src/replicant/audio/ifc_audioout.h b/Src/replicant/audio/ifc_audioout.h
new file mode 100644
index 00000000..1ebd9e19
--- /dev/null
+++ b/Src/replicant/audio/ifc_audioout.h
@@ -0,0 +1,76 @@
+#pragma once
+#include "foundation/dispatch.h"
+#include "foundation/error.h"
+#include "audio/parameters.h"
+
+
+class NOVTABLE ifc_audioout : public Wasabi2::Dispatchable
+{
+protected:
+ ifc_audioout() : Dispatchable(DISPATCHABLE_VERSION) {}
+ ~ifc_audioout() {}
+
+public:
+ enum
+ {
+ CHANNEL_LAYOUT_MICROSOFT = 0x0, // microsoft channel order - http://www.microsoft.com/whdc/device/audio/multichaud.mspx#E4C
+ CHANNEL_LAYOUT_MPEG = 0x1,
+ };
+
+
+ enum
+ {
+ EXTENDED_FLAG_APPLY_GAIN=0x1, /* apply the gain value specified in Parameters::gain */
+ EXTENDED_FLAG_REPLAYGAIN=0x2, /* pass if you tried to figure out ReplayGain on your own. otherwise the Audio Output object will apply the default gain */
+ EXTENDED_FLAG_GAIN_MASK=EXTENDED_FLAG_APPLY_GAIN|EXTENDED_FLAG_REPLAYGAIN, /* a mask to check whether or not the gain value is valid */
+ /* so that you can check if a flag was set that you don't understand */
+ EXTENDED_FLAG_VALID_MASK=EXTENDED_FLAG_APPLY_GAIN|EXTENDED_FLAG_REPLAYGAIN,
+ };
+
+ struct Parameters
+ {
+ size_t sizeof_parameters;
+ nsaudio::Parameters audio;
+ /* anything after this needs sizeof_parameters to be large enough
+ AND a flag set in extended_fields_flags
+ if there's no flag for the field, it's because a default value of 0 can be assumed */
+ unsigned int extended_fields_flags; // set these if you use any of the following fields. see comment above
+ double gain; // additional gain specified by client. usually used for replaygain (so it can be combined with EQ pre-amp or float/pcm conversion)
+ size_t frames_trim_start; // number of frames to trim from the start
+ size_t frames_trim_end; // number of frames to trim from the start
+ };
+
+ int Output(const void *data, size_t data_size) { return AudioOutput_Output(data, data_size); }
+ // returns number of bytes that you can write
+ size_t CanWrite() { return AudioOutput_CanWrite(); }
+ void Flush(double seconds) { AudioOutput_Flush(seconds); }
+ void Pause(int state) { AudioOutput_Pause(state); }
+
+ /* called by the input plugin when no more output will be sent */
+ void Done() { AudioOutput_Done(); }
+ /* called by the input plugin when playback was forcefully stopped */
+ void Stop() { AudioOutput_Stop(); }
+
+ /* returns the latency in seconds (how many seconds until samples you're about to write show up at the audio output */
+ double Latency() { return AudioOutput_Latency(); }
+
+ /* only valid after a call to Done(). Returns NErr_True if there is still data in the buffer, NErr_False otherwise */
+ int Playing() { return AudioOutput_Playing(); }
+
+protected:
+ virtual int WASABICALL AudioOutput_Output(const void *data, size_t data_size)=0;
+ virtual size_t WASABICALL AudioOutput_CanWrite()=0; // returns number of bytes that you can write
+ virtual void WASABICALL AudioOutput_Flush(double seconds)=0;
+ virtual void WASABICALL AudioOutput_Pause(int state)=0;
+
+ /* called by the input plugin when no more output will be sent */
+ virtual void WASABICALL AudioOutput_Done()=0;
+ /* called by the input plugin when playback was forcefully stopped */
+ virtual void WASABICALL AudioOutput_Stop()=0;
+ virtual double WASABICALL AudioOutput_Latency()=0;
+ virtual int WASABICALL AudioOutput_Playing()=0;
+ enum
+ {
+ DISPATCHABLE_VERSION,
+ };
+};
diff --git a/Src/replicant/audio/ifc_equalizer.h b/Src/replicant/audio/ifc_equalizer.h
new file mode 100644
index 00000000..aabe4822
--- /dev/null
+++ b/Src/replicant/audio/ifc_equalizer.h
@@ -0,0 +1,27 @@
+#pragma once
+#include "foundation/dispatch.h"
+
+class ifc_equalizer : public Wasabi2::Dispatchable
+{
+protected:
+ ifc_equalizer() : Dispatchable(DISPATCHABLE_VERSION) {}
+ ~ifc_equalizer() {}
+
+public:
+
+ int SetPreamp(double dB) { return Equalizer_SetPreamp(dB); }
+ int SetBand(unsigned int band, double dB) { return Equalizer_SetBand(band, dB); }
+ int Enable() { return Equalizer_Enable(); }
+ int Disable() { return Equalizer_Disable(); }
+private:
+ virtual int WASABICALL Equalizer_SetPreamp(double dB)=0;
+ virtual int WASABICALL Equalizer_SetBand(unsigned int band, double dB)=0;
+ virtual int WASABICALL Equalizer_Enable()=0;
+ virtual int WASABICALL Equalizer_Disable()=0;
+
+ enum
+ {
+ DISPATCHABLE_VERSION,
+ };
+
+};
diff --git a/Src/replicant/audio/parameters.h b/Src/replicant/audio/parameters.h
new file mode 100644
index 00000000..0e3cad17
--- /dev/null
+++ b/Src/replicant/audio/parameters.h
@@ -0,0 +1,47 @@
+#pragma once
+#include "foundation/types.h"
+#include "foundation/guid.h"
+
+
+namespace nsaudio
+{
+ enum
+ {
+ /* when this is set, the const void * passed to AudioOutput_Output is assumed to be an array of channels,
+ e.g. (format_type == nsaudio_type_float && (format_flags & FORMAT_FLAG_NONINTERLEAVED) && number_of_channels == 2)
+ means that you pass a float *[2] to AudioOutput_Output
+ */
+ FORMAT_FLAG_INTERLEAVED=0x1,
+ FORMAT_FLAG_NONINTERLEAVED=0x2,
+ FORMAT_FLAG_NATIVE_ENDIAN=0x4,
+ FORMAT_FLAG_LITTLE_ENDIAN=0x8, /* audio is SPECIFICALLY little endian (as opposed to native endian on little-endian machines) */
+ FORMAT_FLAG_BIG_ENDIAN=0x10, /* audio is SPECIFICALLY big endian (as opposed to native endian on big-endian machines) */
+ FORMAT_FLAG_SIGNED=0x20, /* e.g. 8 bit PCM is typically unsigned (0-255) */
+ FORMAT_FLAG_UNSIGNED=0x40, /* e.g. 8 bit PCM is typically unsigned (0-255) */
+
+ /* so that you can check if a flag was set that you don't understand */
+ FORMAT_FLAG_VALID_INTERLEAVE = FORMAT_FLAG_INTERLEAVED|FORMAT_FLAG_NONINTERLEAVED,
+ FORMAT_FLAG_VALID_ENDIAN = FORMAT_FLAG_NATIVE_ENDIAN|FORMAT_FLAG_LITTLE_ENDIAN|FORMAT_FLAG_BIG_ENDIAN,
+ FORMAT_FLAG_VALID_SIGNED=FORMAT_FLAG_SIGNED|FORMAT_FLAG_UNSIGNED,
+ FORMAT_FLAG_VALID_MASK=FORMAT_FLAG_VALID_INTERLEAVE|FORMAT_FLAG_VALID_ENDIAN|FORMAT_FLAG_VALID_SIGNED,
+ };
+
+ // {4B80932C-E55F-4969-91EA-772584ABEDC2}
+ static const GUID format_type_pcm =
+ { 0x4b80932c, 0xe55f, 0x4969, { 0x91, 0xea, 0x77, 0x25, 0x84, 0xab, 0xed, 0xc2 } };
+
+ // {6D47717F-A383-4CF8-BB1E-72254BE3F9DC}
+ static const GUID format_type_float =
+ { 0x6d47717f, 0xa383, 0x4cf8, { 0xbb, 0x1e, 0x72, 0x25, 0x4b, 0xe3, 0xf9, 0xdc } };
+
+ struct Parameters
+ {
+ double sample_rate;
+ GUID format_type; // PCM, floating point, SPDIF pass-thru, etc.
+ unsigned int format_flags; // endian, interleaved, signed
+ unsigned int bytes_per_sample; // e.g. 4 for 20bit in a 32bit container
+ unsigned int bits_per_sample; // number of valid bits within the sample
+ unsigned int number_of_channels;
+ unsigned int channel_layout;
+ };
+};
diff --git a/Src/replicant/audio/types.h b/Src/replicant/audio/types.h
new file mode 100644
index 00000000..62a07104
--- /dev/null
+++ b/Src/replicant/audio/types.h
@@ -0,0 +1,44 @@
+#pragma once
+#include "foundation/types.h"
+
+enum Agave_PositionType
+{
+ AGAVE_PLAYPOSITION_100NANOECONDS = 0,
+ AGAVE_PLAYPOSITION_MILLISECONDS = 1,
+ AGAVE_PLAYPOSITION_SECONDS = 2,
+ AGAVE_PLAYPOSITION_HMSF= 3,
+ AGAVE_PLAYPOSITION_SAMPLE_FRAMES = 4,
+ AGAVE_PLAYPOSITION_BYTES = 5,
+ AGAVE_PLAYPOSITION_PACKETS = 6,
+};
+
+struct Agave_HMSF
+{
+ uint8_t hours;
+ uint8_t minutes;
+ uint8_t seconds;
+ uint8_t frames;
+};
+
+union Agave_Position
+{
+ uint64_t nanoseconds100; // in increments of 100 nanoseconds (microsoft style)
+ uint64_t milliseconds;
+ double seconds;
+ Agave_HMSF hmsf;
+ uint64_t sample_frames;
+ uint64_t bytes;
+ uint64_t packets;
+};
+
+struct Agave_Seek
+{
+ Agave_PositionType position_type;
+ Agave_Position position;
+};
+
+static void Agave_Seek_SetBytes(Agave_Seek *seek, uint64_t bytes)
+{
+ seek->position_type=AGAVE_PLAYPOSITION_BYTES;
+ seek->position.bytes = bytes;
+}