summaryrefslogtreecommitdiff
path: root/libs/audiographer/audiographer/general/interleaver.h
blob: c1b5e92cfeaa718803a56840aac325d192d1e08c (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
#ifndef AUDIOGRAPHER_INTERLEAVER_H
#define AUDIOGRAPHER_INTERLEAVER_H

#include "audiographer/visibility.h"
#include "audiographer/types.h"
#include "audiographer/sink.h"
#include "audiographer/exception.h"
#include "audiographer/throwing.h"
#include "audiographer/utils/listed_source.h"

#include <vector>
#include <cmath>

namespace AudioGrapher
{

/// Interleaves many streams of non-interleaved data into one interleaved stream
template<typename T = DefaultSampleType>
class /*LIBAUDIOGRAPHER_API*/ Interleaver
  : public ListedSource<T>
  , public Throwing<>
{
  public:

	/// Constructs an interleaver \n RT safe
	Interleaver()
	  : channels (0)
	  , max_frames (0)
	  , buffer (0)
	{}

	~Interleaver() { reset(); }

	/// Inits the interleaver. Must be called before using. \n Not RT safe
	void init (unsigned int num_channels, framecnt_t max_frames_per_channel)
	{
		reset();
		channels = num_channels;
		max_frames = max_frames_per_channel;

		buffer = new T[channels * max_frames];

		for (unsigned int i = 0; i < channels; ++i) {
			inputs.push_back (InputPtr (new Input (*this, i)));
		}
	}

	/** Returns the input indexed by \a channel \n RT safe
	  * \n The \a process function of returned Sinks are also RT Safe
	  */
	typename Source<T>::SinkPtr input (unsigned int channel)
	{
		if (throw_level (ThrowObject) && channel >= channels) {
			throw Exception (*this, "Channel out of range");
		}

		return boost::static_pointer_cast<Sink<T> > (inputs[channel]);
	}

  private:

	class Input : public Sink<T>
	{
	  public:
		Input (Interleaver & parent, unsigned int channel)
		  : frames_written (0), parent (parent), channel (channel) {}

		void process (ProcessContext<T> const & c)
		{
			if (parent.throw_level (ThrowProcess) && c.channels() > 1) {
				throw Exception (*this, "Data input has more than on channel");
			}
			if (parent.throw_level (ThrowStrict) && frames_written) {
				throw Exception (*this, "Input channels out of sync");
			}
			frames_written = c.frames();
			parent.write_channel (c, channel);
		}

		using Sink<T>::process;

		framecnt_t frames() { return frames_written; }
		void reset() { frames_written = 0; }

	  private:
		framecnt_t frames_written;
		Interleaver & parent;
		unsigned int channel;
	};

	void reset ()
	{
		inputs.clear();
		delete [] buffer;
		buffer = 0;
		channels = 0;
		max_frames = 0;
	}

	void reset_channels ()
	{
		for (unsigned int i = 0; i < channels; ++i) {
			inputs[i]->reset();
		}

	}

	void write_channel (ProcessContext<T> const & c, unsigned int channel)
	{
		if (throw_level (ThrowProcess) && c.frames() > max_frames) {
			reset_channels();
			throw Exception (*this, "Too many frames given to an input");
		}

		for (unsigned int i = 0; i < c.frames(); ++i) {
			buffer[channel + (channels * i)] = c.data()[i];
		}

		framecnt_t const ready_frames = ready_to_output();
		if (ready_frames) {
			ProcessContext<T> c_out (c, buffer, ready_frames, channels);
			ListedSource<T>::output (c_out);
			reset_channels ();
		}
	}

	framecnt_t ready_to_output()
	{
		framecnt_t ready_frames = inputs[0]->frames();
		if (!ready_frames) { return 0; }

		for (unsigned int i = 1; i < channels; ++i) {
			framecnt_t const frames = inputs[i]->frames();
			if (!frames) { return 0; }
			if (throw_level (ThrowProcess) && frames != ready_frames) {
				init (channels, max_frames);
				throw Exception (*this, "Frames count out of sync");
			}
		}
		return ready_frames * channels;
	}

	typedef boost::shared_ptr<Input> InputPtr;
	std::vector<InputPtr> inputs;

	unsigned int channels;
	framecnt_t max_frames;
	T * buffer;
};

} // namespace

#endif // AUDIOGRAPHER_INTERLEAVER_H