1
0
mirror of https://github.com/cookiengineer/audacity synced 2025-06-29 14:48:39 +02:00

bug 11: partial fix: Aggregate Device/Source selection in Device Toolbar

This commit is contained in:
mchinen 2011-01-02 04:06:23 +00:00
parent b1e789122f
commit d22b3b4f5e
4 changed files with 235 additions and 22 deletions

View File

@ -628,7 +628,15 @@ AudioIO::~AudioIO()
delete mThread;
}
void AudioIO::SetMixer(int recordDevice, float recordVolume,
void AudioIO::SetMixer(int inputSource)
{
#if defined(USE_PORTMIXER)
int oldRecordSource = Px_GetCurrentInputSource(mPortMixer);
if ( inputSource != oldRecordSource )
Px_SetCurrentInputSource(mPortMixer, inputSource);
#endif
}
void AudioIO::SetMixer(int inputSource, float recordVolume,
float playbackVolume)
{
mMixerOutputVol = playbackVolume;
@ -638,12 +646,10 @@ void AudioIO::SetMixer(int recordDevice, float recordVolume,
if( mixer )
{
int oldRecordDevice = Px_GetCurrentInputSource(mixer);
float oldRecordVolume = Px_GetInputVolume(mixer);
float oldPlaybackVolume = Px_GetPCMOutputVolume(mixer);
if( recordDevice != oldRecordDevice )
Px_SetCurrentInputSource(mixer, recordDevice);
SetMixer(inputSource);
if( oldRecordVolume != recordVolume )
Px_SetInputVolume(mixer, recordVolume);
if( oldPlaybackVolume != playbackVolume )
@ -859,6 +865,20 @@ void AudioIO::HandleDeviceChange()
if( error )
return;
// Set input source
#if USE_PORTMIXER
int sourceIndex;
if (gPrefs->Read(wxT("/AudioIO/RecordingSourceIndex"), &sourceIndex)) {
if (sourceIndex >= 0) {
//the current index of our source may be different because the stream
//is a combination of two devices, so update it.
sourceIndex = getRecordSourceIndex(mPortMixer);
if (sourceIndex >= 0)
SetMixer(sourceIndex);
}
}
#endif
// Determine mixer capabilities - if it doesn't support control of output
// signal level, we emulate it (by multiplying this value by all outgoing
// samples)
@ -886,6 +906,7 @@ void AudioIO::HandleDeviceChange()
Px_SetInputVolume(mPortMixer, inputVol);
Pa_CloseStream(stream);
#if 0
printf("PortMixer: Output: %s Input: %s\n",
@ -2269,6 +2290,20 @@ int AudioIO::getRecordDevIndex(wxString devName)
return recDeviceNum;
}
#if USE_PORTMIXER
int AudioIO::getRecordSourceIndex(PxMixer *portMixer)
{
int i;
wxString sourceName = gPrefs->Read(wxT("/AudioIO/RecordingSource"), wxT(""));
int numSources = Px_GetNumInputSources(portMixer);
for (i = 0; i < numSources; i++) {
if (sourceName == wxString(Px_GetInputSourceName(portMixer, i), wxConvLocal))
return i;
}
return -1;
}
#endif
int AudioIO::getPlayDevIndex(wxString devName )
{
// if we don't get given a device, look up the preferences

View File

@ -182,6 +182,7 @@ class AUDACITY_DLL_API AudioIO {
* with that stream. If no mixer is available, output is emulated and
* input is stuck at 1.0f (a gain is applied to output samples).
*/
void SetMixer(int inputSource);
void SetMixer(int inputSource, float inputVolume,
float playbackVolume);
void GetMixer(int *inputSource, float *inputVolume,
@ -379,6 +380,13 @@ private:
* default device index.
*/
static int getRecordDevIndex(wxString devName = wxT(""));
/** \brief get the index of the device selected in the preferences.
*
* If the device isn't found, returns -1
*/
#if USE_PORTMIXER
static int getRecordSourceIndex(PxMixer *portMixer);
#endif
/** \brief get the index of the supplied (named) playback device, or the
* device selected in the preferences if none given.

View File

@ -73,6 +73,102 @@ void DeviceToolBar::RecreateTipWindows()
{
}
static wxString MakeDeviceSourceString(DeviceSourceMap *map)
{
wxString ret;
if (map->totalSources <= 1)
ret = map->deviceString;
else
ret = map->deviceString + wxString(": ", wxConvLocal) + map->sourceString;
return ret;
}
static void AddSourcesFromStream(int deviceIndex, wxString &devName, wxArrayString *descs, std::vector<DeviceSourceMap> *maps, PaStream *stream)
{
int i;
PxMixer *portMixer;
DeviceSourceMap map;
map.deviceIndex = deviceIndex;
map.sourceIndex = -1;
map.deviceString = devName;
map.totalSources = 0;
portMixer = Px_OpenMixer(stream, 0);
if (!portMixer) {
maps->push_back(map);
descs->Add(MakeDeviceSourceString(&((*maps)[maps->size() - 1])));
return;
}
//if there is only one source, we don't need to concatenate the source
//or enumerate, because it is something meaningless like 'master'
//(as opposed to 'mic in' or 'line in'), and the user doesn't have any choice.
//note that some devices have no input sources at all but are still valid.
//the behavior we do is the same for 0 and 1 source cases.
map.totalSources = Px_GetNumInputSources(portMixer);
if (map.totalSources <= 1) {
map.sourceIndex = 0;
maps->push_back(map);
descs->Add(MakeDeviceSourceString(&((*maps)[maps->size() - 1])));
} else {
//open up a stream with the device so portmixer can get the info out of it.
for (i = 0; i < map.totalSources; i++) {
map.sourceIndex = i;
map.sourceString = wxString(Px_GetInputSourceName(portMixer, i), wxConvLocal);
maps->push_back(map);
descs->Add(MakeDeviceSourceString(&((*maps)[maps->size() - 1])));
}
}
Px_CloseMixer(portMixer);
}
static void AddSources(int deviceIndex, int rate, wxArrayString *descs, std::vector<DeviceSourceMap> *maps, int isInput)
{
int error;
DeviceSourceMap map;
wxString devName;
devName = DeviceName(Pa_GetDeviceInfo(deviceIndex));
// This tries to open the device with the samplerate worked out above, which
// will be the highest available for play and record on the device, or
// 44.1kHz if the info cannot be fetched.
PaStream *stream = NULL;
PaStreamParameters parameters;
parameters.device = deviceIndex;
parameters.sampleFormat = paFloat32;
parameters.hostApiSpecificStreamInfo = NULL;
parameters.channelCount = 1;
if (Pa_GetDeviceInfo(deviceIndex))
parameters.suggestedLatency = isInput ?
Pa_GetDeviceInfo(deviceIndex)->defaultLowInputLatency:
Pa_GetDeviceInfo(deviceIndex)->defaultLowOutputLatency;
else
parameters.suggestedLatency = DEFAULT_LATENCY_CORRECTION/1000.0;
// try opening for record and playback
error = Pa_OpenStream(&stream,
isInput ? &parameters : NULL,
isInput ? NULL : &parameters,
rate, paFramesPerBufferUnspecified,
paClipOff | paDitherOff,
audacityAudioCallback, NULL);
if (stream) {
AddSourcesFromStream(deviceIndex, devName, descs, maps, stream);
Pa_CloseStream(stream);
} else {
map.deviceIndex = deviceIndex;
map.sourceIndex = -1;
map.deviceString = devName;
map.totalSources = 0;
maps->push_back(map);
descs->Add(MakeDeviceSourceString(&((*maps)[maps->size() - 1])));
}
}
void DeviceToolBar::Populate()
{
int i;
@ -81,16 +177,17 @@ void DeviceToolBar::Populate()
int nDevices = Pa_GetDeviceCount();
//The heirarchy for devices is Host/device/source.
//Some newer systems aggregate this.
//So we need to call port mixer for every device to get the sources
for (i = 0; i < nDevices; i++) {
const PaDeviceInfo *info = Pa_GetDeviceInfo(i);
wxString name = DeviceName(info);
if (info->maxOutputChannels > 0) {
outputs.Add(name);
AddSources(i, info->defaultSampleRate, &outputs, &mOutputDeviceSourceMaps, 0);
}
if (info->maxInputChannels > 0) {
inputs.Add(name);
AddSources(i, info->defaultSampleRate, &inputs, &mInputDeviceSourceMaps, 1);
}
}
@ -187,8 +284,25 @@ void DeviceToolBar::OnCaptureKey(wxCommandEvent &event)
void DeviceToolBar::UpdatePrefs()
{
mInput->SetStringSelection(gPrefs->Read(wxT("/AudioIO/RecordingDevice"), wxT("")));
mOutput->SetStringSelection(gPrefs->Read(wxT("/AudioIO/PlaybackDevice"), wxT("")));
wxString devName;
wxString sourceName;
wxString desc;
devName = gPrefs->Read(wxT("/AudioIO/RecordingDevice"), wxT(""));
sourceName = gPrefs->Read(wxT("/AudioIO/RecordingSource"), wxT(""));
if (sourceName == wxT(""))
desc = devName;
else
desc = devName + wxString(": ", wxConvLocal) + sourceName;
mInput->SetStringSelection(desc);
devName = gPrefs->Read(wxT("/AudioIO/PlaybackDevice"), wxT(""));
sourceName = gPrefs->Read(wxT("/AudioIO/PlaybackSource"), wxT(""));
if (sourceName == wxT(""))
desc = devName;
else
desc = devName + wxString(": ", wxConvLocal) + sourceName;
mOutput->SetStringSelection(desc);
RegenerateTooltips();
@ -209,15 +323,27 @@ void DeviceToolBar::RegenerateTooltips()
void DeviceToolBar::OnChoice(wxCommandEvent &event)
{
int inputSelectionIndex;
int outputSelectionIndex;
inputSelectionIndex = mInput->GetSelection();
outputSelectionIndex = mOutput->GetSelection();
wxString oldInput = gPrefs->Read(wxT("/AudioIO/RecordingDevice"), wxT(""));
wxString newInput = mInput->GetString(mInput->GetSelection());
wxString newInput = inputSelectionIndex >= 0 ?
mInputDeviceSourceMaps[inputSelectionIndex].deviceString:
oldInput;
wxString oldOutput = gPrefs->Read(wxT("/AudioIO/PlaybackDevice"), wxT(""));
wxString newOutput = mOutput->GetString(mOutput->GetSelection());
wxString newOutput = outputSelectionIndex >= 0 ?
mOutputDeviceSourceMaps[outputSelectionIndex].deviceString:
oldOutput;
int oldInIndex = -1, newInIndex = -1, oldOutIndex = -1, newOutIndex = -1;
int nDevices = Pa_GetDeviceCount();
int i;
bool foundCompatibleDevice = false;
// Find device indices for input and output
for (int i = 0; i < nDevices; ++i)
for (i = 0; i < nDevices; ++i)
{
const PaDeviceInfo *info = Pa_GetDeviceInfo(i);
wxString name = DeviceName(info);
@ -242,6 +368,10 @@ void DeviceToolBar::OnChoice(wxCommandEvent &event)
// We changed input; be sure the output device has the same API
if (inInfo->hostApi != outInfo->hostApi) {
// First try setting the same device as the input
// I think it is okay to set the string selection as devicename
// because I *believe* that the input sources only refer to inputs
// but if we end up with blanks in the combobox post selection due
// to api mismatch, then this needs to change.
if (!mOutput->SetStringSelection(DeviceName(inInfo)))
{
// Not found; set output device to default for the API
@ -256,28 +386,56 @@ void DeviceToolBar::OnChoice(wxCommandEvent &event)
// We changed output; be sure the input device has the same API
if (outInfo->hostApi != inInfo->hostApi) {
// First try setting the same device as the output
if (!mInput->SetStringSelection(DeviceName(outInfo)))
{
for (i = 0; i < (int)mInputDeviceSourceMaps.size(); i++) {
if (mInputDeviceSourceMaps[i].deviceString == DeviceName(outInfo)) {
mInput->SetStringSelection(MakeDeviceSourceString(&mInputDeviceSourceMaps[i]));
foundCompatibleDevice = true;
break;
}
}
if (!foundCompatibleDevice) {
// Not found; set input device to default for the API
const PaHostApiInfo *apiInfo = Pa_GetHostApiInfo(outInfo->hostApi);
inInfo = Pa_GetDeviceInfo(apiInfo->defaultInputDevice);
mInput->SetStringSelection(DeviceName(inInfo));
for (i = 0; i < (int)mInputDeviceSourceMaps.size(); i++) {
if (mInputDeviceSourceMaps[i].deviceString == DeviceName(inInfo)) {
break;
}
}
mInput->SetStringSelection(MakeDeviceSourceString(&mInputDeviceSourceMaps[i]));
}
}
}
inputSelectionIndex = mInput->GetSelection();
outputSelectionIndex = mOutput->GetSelection();
gPrefs->Write(wxT("/AudioIO/Host"),
wxString(Pa_GetHostApiInfo(inInfo->hostApi)->name, wxConvLocal));
gPrefs->Write(wxT("/AudioIO/RecordingDevice"),
mInput->GetString(mInput->GetSelection()));
gPrefs->Write(wxT("/AudioIO/PlaybackDevice"),
mOutput->GetString(mOutput->GetSelection()));
if (inputSelectionIndex >= 0) {
gPrefs->Write(wxT("/AudioIO/RecordingDevice"),
mInputDeviceSourceMaps[inputSelectionIndex].deviceString);
gPrefs->Write(wxT("/AudioIO/RecordingSourceIndex"),
mInputDeviceSourceMaps[inputSelectionIndex].sourceIndex);
if (mInputDeviceSourceMaps[inputSelectionIndex].sourceIndex >= 0) {
gPrefs->Write(wxT("/AudioIO/RecordingSource"),
mInputDeviceSourceMaps[inputSelectionIndex].sourceString);
} else
gPrefs->Write(wxT("/AudioIO/RecordingSource"), wxT(""));
}
if (outputSelectionIndex >= 0) {
gPrefs->Write(wxT("/AudioIO/PlaybackDevice"),
mOutputDeviceSourceMaps[outputSelectionIndex].deviceString);
if (mOutputDeviceSourceMaps[outputSelectionIndex].sourceIndex >= 0) {
gPrefs->Write(wxT("/AudioIO/PlaybackSource"),
mOutputDeviceSourceMaps[outputSelectionIndex].sourceString);
} else
gPrefs->Write(wxT("/AudioIO/RecordingSource"), wxT(""));
}
if (gAudioIO)
gAudioIO->HandleDeviceChange();
GetActiveProject()->UpdatePrefs();
}

View File

@ -11,6 +11,7 @@
#ifndef __AUDACITY_DEVICE_TOOLBAR__
#define __AUDACITY_DEVICE_TOOLBAR__
#include <vector>
#include "ToolBar.h"
class wxImage;
@ -18,6 +19,14 @@ class wxSize;
class wxPoint;
class wxChoice;
typedef struct DeviceSourceMap {
int deviceIndex;
int sourceIndex;
int totalSources;
wxString sourceString;
wxString deviceString;
} DeviceSourceMap;
class DeviceToolBar:public ToolBar {
public:
@ -48,6 +57,9 @@ class DeviceToolBar:public ToolBar {
wxChoice *mInput;
wxChoice *mOutput;
std::vector<DeviceSourceMap> mInputDeviceSourceMaps;
std::vector<DeviceSourceMap> mOutputDeviceSourceMaps;
public:
DECLARE_CLASS(DeviceToolBar);