David Shipman started implementing the BeOS system audio mixer.
git-svn-id: file:///srv/svn/repos/haiku/trunk/current@681 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,208 @@
|
|||||||
|
// AudioMixer.h
|
||||||
|
/*
|
||||||
|
|
||||||
|
By David Shipman, 2002
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef _AUDIOMIXER_H
|
||||||
|
#define _AUDIOMIXER_H
|
||||||
|
|
||||||
|
// forward declarations
|
||||||
|
|
||||||
|
class input_channel;
|
||||||
|
|
||||||
|
// includes
|
||||||
|
|
||||||
|
#include <media/BufferGroup.h>
|
||||||
|
#include <media/MediaNode.h>
|
||||||
|
#include <media/BufferConsumer.h>
|
||||||
|
#include <media/BufferProducer.h>
|
||||||
|
#include <media/Controllable.h>
|
||||||
|
#include <media/MediaEventLooper.h>
|
||||||
|
|
||||||
|
// ----------------
|
||||||
|
// AudioMixer class
|
||||||
|
|
||||||
|
class AudioMixer :
|
||||||
|
public BBufferConsumer,
|
||||||
|
public BBufferProducer,
|
||||||
|
public BControllable,
|
||||||
|
public BMediaEventLooper
|
||||||
|
{
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
AudioMixer(BMediaAddOn *addOn);
|
||||||
|
~AudioMixer();
|
||||||
|
|
||||||
|
// Our own (AudioMixer) methods
|
||||||
|
|
||||||
|
bool IsValidDest( media_destination dest);
|
||||||
|
bool IsValidSource( media_source source);
|
||||||
|
|
||||||
|
status_t ResolveDest(int32 dest, input_channel *outchannel);
|
||||||
|
status_t ResolveDest(media_destination dest, input_channel *outchannel);
|
||||||
|
|
||||||
|
void AllocateBuffers();
|
||||||
|
|
||||||
|
// BMediaNode methods
|
||||||
|
|
||||||
|
BMediaAddOn * AddOn(int32*) const;
|
||||||
|
// void SetRunMode(run_mode);
|
||||||
|
// void Preroll();
|
||||||
|
// void SetTimeSource(BTimeSource* time_source);
|
||||||
|
// status_t RequestCompleted(const media_request_info & info);
|
||||||
|
|
||||||
|
protected:
|
||||||
|
|
||||||
|
// BControllable methods
|
||||||
|
|
||||||
|
status_t GetParameterValue( int32 id, bigtime_t* last_change,
|
||||||
|
void* value,
|
||||||
|
size_t* ioSize);
|
||||||
|
|
||||||
|
void SetParameterValue( int32 id, bigtime_t when,
|
||||||
|
const void* value,
|
||||||
|
size_t size);
|
||||||
|
|
||||||
|
// BBufferConsumer methods
|
||||||
|
|
||||||
|
status_t HandleMessage( int32 message,
|
||||||
|
const void* data,
|
||||||
|
size_t size);
|
||||||
|
|
||||||
|
status_t AcceptFormat( const media_destination &dest, media_format *format);
|
||||||
|
|
||||||
|
status_t GetNextInput( int32 *cookie,
|
||||||
|
media_input *out_input);
|
||||||
|
|
||||||
|
void DisposeInputCookie( int32 cookie);
|
||||||
|
|
||||||
|
void BufferReceived( BBuffer *buffer);
|
||||||
|
|
||||||
|
void ProducerDataStatus( const media_destination &for_whom,
|
||||||
|
int32 status,
|
||||||
|
bigtime_t at_performance_time);
|
||||||
|
|
||||||
|
status_t GetLatencyFor( const media_destination &for_whom,
|
||||||
|
bigtime_t *out_latency,
|
||||||
|
media_node_id *out_timesource);
|
||||||
|
|
||||||
|
status_t Connected( const media_source &producer,
|
||||||
|
const media_destination &where,
|
||||||
|
const media_format &with_format,
|
||||||
|
media_input *out_input);
|
||||||
|
|
||||||
|
void Disconnected( const media_source &producer,
|
||||||
|
const media_destination &where);
|
||||||
|
|
||||||
|
status_t FormatChanged( const media_source &producer,
|
||||||
|
const media_destination &consumer,
|
||||||
|
int32 change_tag,
|
||||||
|
const media_format &format);
|
||||||
|
|
||||||
|
|
||||||
|
//
|
||||||
|
// BBufferProducer methods
|
||||||
|
//
|
||||||
|
|
||||||
|
status_t FormatSuggestionRequested( media_type type,
|
||||||
|
int32 quality,
|
||||||
|
media_format *format);
|
||||||
|
|
||||||
|
status_t FormatProposal(
|
||||||
|
const media_source& output,
|
||||||
|
media_format* format);
|
||||||
|
//
|
||||||
|
// /* If the format isn't good, put a good format into *io_format and return error */
|
||||||
|
// /* If format has wildcard, specialize to what you can do (and change). */
|
||||||
|
// /* If you can change the format, return OK. */
|
||||||
|
// /* The request comes from your destination sychronously, so you cannot ask it */
|
||||||
|
// /* whether it likes it -- you should assume it will since it asked. */
|
||||||
|
status_t FormatChangeRequested(
|
||||||
|
const media_source& source,
|
||||||
|
const media_destination& destination,
|
||||||
|
media_format* io_format,
|
||||||
|
int32* _deprecated_);
|
||||||
|
|
||||||
|
status_t GetNextOutput( /* cookie starts as 0 */
|
||||||
|
int32* cookie,
|
||||||
|
media_output* out_output);
|
||||||
|
|
||||||
|
status_t DisposeOutputCookie(
|
||||||
|
int32 cookie);
|
||||||
|
|
||||||
|
status_t SetBufferGroup(
|
||||||
|
const media_source& for_source,
|
||||||
|
BBufferGroup* group);
|
||||||
|
|
||||||
|
status_t GetLatency(
|
||||||
|
bigtime_t* out_latency);
|
||||||
|
|
||||||
|
status_t PrepareToConnect(
|
||||||
|
const media_source& what,
|
||||||
|
const media_destination& where,
|
||||||
|
media_format* format,
|
||||||
|
media_source* out_source,
|
||||||
|
char* out_name);
|
||||||
|
|
||||||
|
void Connect(
|
||||||
|
status_t error,
|
||||||
|
const media_source& source,
|
||||||
|
const media_destination& destination,
|
||||||
|
const media_format& format,
|
||||||
|
char* io_name);
|
||||||
|
|
||||||
|
void Disconnect(
|
||||||
|
const media_source& what,
|
||||||
|
const media_destination& where);
|
||||||
|
|
||||||
|
void LateNoticeReceived(
|
||||||
|
const media_source& what,
|
||||||
|
bigtime_t how_much,
|
||||||
|
bigtime_t performance_time);
|
||||||
|
|
||||||
|
void EnableOutput(
|
||||||
|
const media_source & what,
|
||||||
|
bool enabled,
|
||||||
|
int32* _deprecated_);
|
||||||
|
|
||||||
|
// BMediaEventLooper methods
|
||||||
|
|
||||||
|
virtual void NodeRegistered();
|
||||||
|
|
||||||
|
void HandleEvent( const media_timed_event *event,
|
||||||
|
bigtime_t lateness,
|
||||||
|
bool realTimeEvent = false);
|
||||||
|
|
||||||
|
// handle mixing in separate thread
|
||||||
|
// not implemented (yet)
|
||||||
|
|
||||||
|
static int32 _mix_thread_(void *data);
|
||||||
|
int32 MixThread();
|
||||||
|
|
||||||
|
private:
|
||||||
|
|
||||||
|
thread_id fThread; // for launching mixthread
|
||||||
|
|
||||||
|
media_input fInput; // descriptor of single input (temp)
|
||||||
|
media_output fOutput; // single output (temp)
|
||||||
|
|
||||||
|
BMediaAddOn * fAddOn;
|
||||||
|
BParameterWeb * fWeb; // local pointer to parameterweb
|
||||||
|
|
||||||
|
bigtime_t fLatency, fInternalLatency; // latency (total and internal)
|
||||||
|
bigtime_t fStartTime, fNextEventTime; // time node started, time of next (output) event
|
||||||
|
uint64 fFramesSent; // audio frames sent
|
||||||
|
bool fOutputEnabled;
|
||||||
|
|
||||||
|
media_format fPrefInputFormat, fPrefOutputFormat;
|
||||||
|
BBufferGroup * fBufferGroup;
|
||||||
|
|
||||||
|
|
||||||
|
BList fInputChannels;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
||||||
@@ -0,0 +1,27 @@
|
|||||||
|
// IOStructures.h
|
||||||
|
/*
|
||||||
|
|
||||||
|
Structures to represent IO channels in the AudioMixer node
|
||||||
|
By David Shipman, 2002
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef _IO_STRUCT_H
|
||||||
|
#define _IO_STRUCT_H
|
||||||
|
|
||||||
|
struct input_channel
|
||||||
|
{
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
media_input fInput;
|
||||||
|
int32 fProducerDataStatus; // status of upstream data
|
||||||
|
bool DataAvailable; // there is a buffer ready for mixing
|
||||||
|
char * fData;
|
||||||
|
|
||||||
|
size_t fEventOffset; // offset (in bytes)
|
||||||
|
size_t fDataSize; // size of ringbuffer
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif;
|
||||||
@@ -0,0 +1,84 @@
|
|||||||
|
// MixerAddOn.cpp
|
||||||
|
//
|
||||||
|
// David Shipman, 2002
|
||||||
|
// allows AudioMixer to be used as an addon
|
||||||
|
//
|
||||||
|
|
||||||
|
#include "AudioMixer.h"
|
||||||
|
#include "MixerAddOn.h"
|
||||||
|
#include <cstring>
|
||||||
|
#include <cstdlib>
|
||||||
|
|
||||||
|
// instantiation function
|
||||||
|
extern "C" _EXPORT BMediaAddOn* make_media_addon(image_id image) {
|
||||||
|
return new AudioMixerAddon(image);
|
||||||
|
}
|
||||||
|
|
||||||
|
// -------------------------------------------------------- //
|
||||||
|
// ctor/dtor
|
||||||
|
// -------------------------------------------------------- //
|
||||||
|
|
||||||
|
AudioMixerAddon::~AudioMixerAddon() {}
|
||||||
|
AudioMixerAddon::AudioMixerAddon(image_id image) :
|
||||||
|
BMediaAddOn(image) {}
|
||||||
|
|
||||||
|
// -------------------------------------------------------- //
|
||||||
|
// BMediaAddOn impl
|
||||||
|
// -------------------------------------------------------- //
|
||||||
|
|
||||||
|
status_t AudioMixerAddon::InitCheck(
|
||||||
|
const char** out_failure_text) {
|
||||||
|
return B_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
int32 AudioMixerAddon::CountFlavors() {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
status_t AudioMixerAddon::GetFlavorAt(
|
||||||
|
int32 n,
|
||||||
|
const flavor_info** out_info) {
|
||||||
|
if(n)
|
||||||
|
return B_ERROR;
|
||||||
|
|
||||||
|
flavor_info* pInfo = new flavor_info;
|
||||||
|
pInfo->internal_id = n;
|
||||||
|
pInfo->name = "AudioMixer";
|
||||||
|
pInfo->info =
|
||||||
|
"AudioMixer media addon\n";
|
||||||
|
"By David Shipman, 2002";
|
||||||
|
pInfo->kinds = B_BUFFER_PRODUCER | B_BUFFER_CONSUMER | B_SYSTEM_MIXER | B_CONTROLLABLE;
|
||||||
|
pInfo->flavor_flags = 0;
|
||||||
|
pInfo->possible_count = 0;
|
||||||
|
|
||||||
|
media_format* pFormat = new media_format;
|
||||||
|
pFormat->type = B_MEDIA_RAW_AUDIO;
|
||||||
|
pFormat->u.raw_audio = media_raw_audio_format::wildcard;
|
||||||
|
|
||||||
|
pInfo->in_format_count = 1;
|
||||||
|
pInfo->in_formats = pFormat;
|
||||||
|
|
||||||
|
pInfo->out_format_count = 1;
|
||||||
|
pInfo->out_formats = pFormat;
|
||||||
|
|
||||||
|
*out_info = pInfo;
|
||||||
|
return B_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
BMediaNode* AudioMixerAddon::InstantiateNodeFor(
|
||||||
|
const flavor_info* info,
|
||||||
|
BMessage* config,
|
||||||
|
status_t* out_error) {
|
||||||
|
|
||||||
|
return new AudioMixer(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
status_t AudioMixerAddon::GetConfigurationFor(
|
||||||
|
BMediaNode* your_node,
|
||||||
|
BMessage* into_message) {
|
||||||
|
|
||||||
|
// no config yet
|
||||||
|
return B_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
// end
|
||||||
@@ -0,0 +1,45 @@
|
|||||||
|
// MixerAddon.h
|
||||||
|
// David Shipman, 2002
|
||||||
|
//
|
||||||
|
// Quick addon header for the Audio Mixer
|
||||||
|
//
|
||||||
|
|
||||||
|
#ifndef __AudioMixerAddOn_H_
|
||||||
|
#define __AudioMixerAddOn_H_
|
||||||
|
|
||||||
|
#include <MediaAddOn.h>
|
||||||
|
|
||||||
|
// -------------------------------------------------------- //
|
||||||
|
|
||||||
|
class AudioMixerAddon :
|
||||||
|
public BMediaAddOn {
|
||||||
|
typedef BMediaAddOn _inherited;
|
||||||
|
|
||||||
|
public: // ctor/dtor
|
||||||
|
virtual ~AudioMixerAddon();
|
||||||
|
explicit AudioMixerAddon(image_id image);
|
||||||
|
|
||||||
|
public: // BMediaAddOn impl
|
||||||
|
virtual status_t InitCheck(
|
||||||
|
const char** out_failure_text);
|
||||||
|
virtual int32 CountFlavors();
|
||||||
|
virtual status_t GetFlavorAt(
|
||||||
|
int32 n,
|
||||||
|
const flavor_info ** out_info);
|
||||||
|
virtual BMediaNode * InstantiateNodeFor(
|
||||||
|
const flavor_info * info,
|
||||||
|
BMessage * config,
|
||||||
|
status_t * out_error);
|
||||||
|
virtual status_t GetConfigurationFor(
|
||||||
|
BMediaNode * your_node,
|
||||||
|
BMessage * into_message);
|
||||||
|
|
||||||
|
virtual bool WantsAutoStart() { return false; }
|
||||||
|
virtual status_t AutoStart(
|
||||||
|
int in_count,
|
||||||
|
BMediaNode ** out_node,
|
||||||
|
int32 * out_internal_id,
|
||||||
|
bool * out_has_more) { return B_OK; }
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif /*__AudioMixerAddOn_H_*/
|
||||||
@@ -0,0 +1,31 @@
|
|||||||
|
----------------------
|
||||||
|
Be Sample Code License
|
||||||
|
----------------------
|
||||||
|
|
||||||
|
Copyright 1991-1999, Be Incorporated.
|
||||||
|
All rights reserved.
|
||||||
|
|
||||||
|
Redistribution and use in source and binary forms, with or without
|
||||||
|
modification, are permitted provided that the following conditions
|
||||||
|
are met:
|
||||||
|
|
||||||
|
1. Redistributions of source code must retain the above copyright
|
||||||
|
notice, this list of conditions, and the following disclaimer.
|
||||||
|
|
||||||
|
2. Redistributions in binary form must reproduce the above copyright
|
||||||
|
notice, this list of conditions, and the following disclaimer in the
|
||||||
|
documentation and/or other materials provided with the distribution.
|
||||||
|
|
||||||
|
3. The name of the author may not be used to endorse or promote products
|
||||||
|
derived from this software without specific prior written permission.
|
||||||
|
|
||||||
|
THIS SOFTWARE IS PROVIDED BY THE AUTHOR "AS IS" AND ANY EXPRESS OR
|
||||||
|
IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
||||||
|
OF TITLE, NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||||
|
PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
|
||||||
|
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||||
|
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||||
|
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
|
||||||
|
AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
|
||||||
|
TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||||
|
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
@@ -0,0 +1,41 @@
|
|||||||
|
OpenBeOS Audio Mixer
|
||||||
|
David Shipman, 2002
|
||||||
|
|
||||||
|
Overview
|
||||||
|
|
||||||
|
The node is based on a mediaeventlooper, using the HandleEvent loop to compile
|
||||||
|
the mixed buffer output.
|
||||||
|
Each input creates a ringbuffer (fixed size "looped" buffer) for its input - this way
|
||||||
|
buffer contents can be placed in the ringbuffer and recycled immediately.
|
||||||
|
|
||||||
|
Inputs are maintained using a list of input_channel structs - inputs are created/
|
||||||
|
destroyed dynamically when producers connect/disconnect.
|
||||||
|
|
||||||
|
Done
|
||||||
|
|
||||||
|
- node functions as a replacement for the BeOS R5 mixer.media-addon
|
||||||
|
- IO/conversion between stereo B_AUDIO_FLOAT and B_AUDIO_SHORT streams
|
||||||
|
|
||||||
|
Tested
|
||||||
|
|
||||||
|
- Output to emu10k1 (SBLive)
|
||||||
|
- Input from CL-Amp and emu10k1-in
|
||||||
|
|
||||||
|
Bugs
|
||||||
|
|
||||||
|
- Soundplay, extractor nodes, and a variety of others connect, but sound really bad!
|
||||||
|
- When input stops, the current ringbuffer contents continue to be played
|
||||||
|
|
||||||
|
To Do (lots) (vaguely in order of importance)
|
||||||
|
|
||||||
|
- fix bugs
|
||||||
|
- support for remaining formats
|
||||||
|
- samplerate conversion
|
||||||
|
- Interface (BControllable)
|
||||||
|
- multithreading (separate mix thread)
|
||||||
|
- tidy up code
|
||||||
|
|
||||||
|
Notes :
|
||||||
|
|
||||||
|
Parts of this program are based on code under the Be Sample Code License.
|
||||||
|
This is included in the archive, in accordance with the license.
|
||||||
Reference in New Issue
Block a user