我正在尝试在PortAudio中创建一个音乐可视化应用程序,我做了一些基础研究,并找到了一些关于如何从麦克风录制到(临时)文件的示例。但是没有例子说明在录制过程中数据没有用于运行时。
那么如何开始连续的音频流,我可以从当前的“帧”中捕获数据?
这就是我尝试这样做的方式:
#include <stdio.h>
#include <unistd.h>
#include <time.h>
#include <stdlib.h>
#include "portaudio.h"
#define SAMPLE_RATE (44100)
typedef struct{
int frameIndex;
int maxFrameIndex;
char* recordedSamples;
}
testData;
PaStream* stream;
static int recordCallback(const void* inputBuffer, void* outputBuffer, unsigned long frameCount, const PaStreamCallbackTimeInfo* timeInfo, PaStreamCallbackFlags statusFlags, void* userData){
testData* data = (testData*)userData;
const char* buffer_ptr = (const char*)inputBuffer;
char* index_ptr = &data->recordedSamples[data->frameIndex];
long framesToCalc;
long i;
int finished;
unsigned long framesLeft = data->maxFrameIndex - data->frameIndex;
if(framesLeft < frameCount){
framesToCalc = framesLeft;
finished = paComplete;
}else{
framesToCalc = frameCount;
finished = paContinue;
}
if(inputBuffer == NULL){
for(i = 0; i < framesToCalc; i++){
*index_ptr++ = 0;
}
}else{
for(i = 0; i < framesToCalc; i++){
*index_ptr++ = *buffer_ptr++;
}
}
data->frameIndex += framesToCalc;
return finished;
}
int setup(testData streamData){
PaError err;
err = Pa_Initialize();
if(err != paNoError){
fprintf(stderr, "Pa_Initialize error: %s\n", Pa_GetErrorText(err));
return 1;
}
PaStreamParameters inputParameters;
inputParameters.device = Pa_GetDefaultInputDevice();
if (inputParameters.device == paNoDevice) {
fprintf(stderr, "Error: No default input device.\n");
return 1;
}
inputParameters.channelCount = 1;
inputParameters.sampleFormat = paInt8;
inputParameters.suggestedLatency = Pa_GetDeviceInfo(inputParameters.device)->defaultLowInputLatency;
inputParameters.hostApiSpecificStreamInfo = NULL;
err = Pa_OpenStream(&stream, &inputParameters, NULL, SAMPLE_RATE, 256, paClipOff, recordCallback, &streamData);
if(err != paNoError){
fprintf(stderr, "Pa_OpenDefaultStream error: %s\n", Pa_GetErrorText(err));
return 1;
}
err = Pa_StartStream(stream);
if(err != paNoError){
fprintf(stderr, "Pa_StartStream error: %s\n", Pa_GetErrorText(err));
return 1;
}
return 0;
}
void quit(testData streamData){
PaError err;
err = Pa_Terminate();
if(err != paNoError){
fprintf(stderr, "Pa_Terminate error: %s\n", Pa_GetErrorText(err));
}
if(streamData.recordedSamples)
free(streamData.recordedSamples);
}
int main(){
int i;
PaError err;
testData streamData = {0};
streamData.frameIndex = 0;
streamData.maxFrameIndex = SAMPLE_RATE;
streamData.recordedSamples = (char*)malloc(SAMPLE_RATE * sizeof(char));
if(streamData.recordedSamples == NULL)
printf("Could not allocate record array.\n");
for(i=0; i<SAMPLE_RATE; i++)
streamData.recordedSamples[i] = 0;
//int totalFrames = SAMPLE_RATE;
if(!setup(streamData)){
printf("Opened\n");
int i = 0;
while(i++ < 500){
if((err = Pa_GetStreamReadAvailable(stream)) != paNoError)
break;
while((err = Pa_IsStreamActive(stream)) == 1){
Pa_Sleep(1000);
}
err = Pa_CloseStream(stream);
if(err != paNoError)
break;
streamData.frameIndex = 0;
for(i=0; i<SAMPLE_RATE; i++)
streamData.recordedSamples[i] = 0;
}
if(err != paNoError){
fprintf(stderr, "Active stream error: %s\n", Pa_GetErrorText(err));
}
quit(streamData);
}else{
puts("Couldn't open\n");
}
return 0;
}
但是它提供了以下输出:
ALSA lib pcm.c:2217:(snd_pcm_open_noupdate) Unknown PCM cards.pcm.rear
ALSA lib pcm.c:2217:(snd_pcm_open_noupdate) Unknown PCM cards.pcm.center_lfe
ALSA lib pcm.c:2217:(snd_pcm_open_noupdate) Unknown PCM cards.pcm.side
ALSA lib pcm_dmix.c:957:(snd_pcm_dmix_open) The dmix plugin supports only playback stream
Active stream error: Can't read from a callback stream
答案 0 :(得分:4)
更新:
此代码的目的是什么?
if((err = Pa_GetStreamReadAvailable(stream)) != paNoError)
break;
在我看来,这会导致您的(最新)问题。为什么需要检索(然后丢弃)可以从流中读取的帧数而不等待,因为stream是回调流,所以可能会为零?
<小时/> 上一个回答:
这似乎非常可疑:
static void* data;
/* ... */
static int recordCallback(const void* inputBuffer, void* outputBuffer, unsigned long frameCount, const PaStreamCallbackTimeInfo* timeInfo, PaStreamCallbackFlags statusFlags, void* userData){
testData* data = (testData*)userData;
/* ... */
}
首先,为什么有两个名为data
的变量?那真是太傻了......你能想到更合适的标识符吗?
其次,您将&data
(a void **
)传递给Pa_OpenStream。据推测,Pa_OpenStream将相同的值传递给您的回调函数,您将该指针视为void *
,就好像它指向testData *
一样。这是未定义的行为。
删除 。这不是必要的。在main中声明一个新的static void* data;
testData data = { 0 };
,在最顶部。现在您将testData *
(转换为void *
)传递给Pa_OpenStream,Pa_OpenStream会将其传递给您的回调,您可以安全地将其转换回testData *
。您可能希望在调用Pa_OpenStream ...
data
的成员
答案 1 :(得分:1)
要实时与数据流交互,您需要一种机制,可以在帧周期内休眠(在Windows上忙等待)(采样率/每帧采样数)或使用线程同步原语来触发您的线程{ {1}}已准备好处理音频。这将使您可以访问PortAudio在其回调期间提供的每个数据帧(从PortAudio线程调用)。以下是使用int main
和boost::condition
概念的概念。
boost::mutex
通常,此技术是跨平台的,但此特定实现可能仅适用于Linux。在Windows上使用//CAUTION THIS SNIPPET IS ONLY INTENDED TO DEMONSTRATE HOW ONE MIGHT
//SYNCHRONIZE WITH THE PORTAUDIO THREAD
#include <stdio.h>
#include <unistd.h>
#include <time.h>
#include <stdlib.h>
#include <boost/thread/thread.hpp>
#include <boost/thread/mutex.hpp>
#include <boost/thread/condition.hpp>
#include <boost/interprocess/sync/scoped_lock.hpp>
#include "portaudio.h"
boost::condition waitForAudio;
boost::mutex waitForAudioMutex;
boost::mutex audioBufferMutex;
bool trigger = false;
std::deque<char> audioBuffer;
static int recordCallback(const void* inputBuffer, void* outputBuffer, unsigned long frameCount, const PaStreamCallbackTimeInfo* timeInfo, PaStreamCallbackFlags statusFlags, void* userData){
const char* buffer_ptr = (const char*)inputBuffer;
//Lock mutex to block user thread from modifying data buffer
audioBufferMutex.lock();
//Copy data to user buffer
for(i = 0; i < frameCount; ++i) {
audioBuffer.push_back(buffer_ptr + i);
}
//Unlock mutex, allow user to manipulate buffer
audioBufferMutex.unlock();
//Signal user thread to process audio
waitForAudioMutex.lock();
trigger= true;
waitForAudio.notify_one();
waitForAudioMutex.unlock();
return finished;
}
int main(){
Pa_Initialize();
//OPEN AND START PORTAUDIO STREAM
while(true){ //Catch signal (Ctrl+C) or some other mechanism to interrupt this loop
boost::xtime duration;
boost::xtime_get(&duration, boost::TIME_UTC);
boost::interprocess::scoped_lock<boost::mutex> lock(waitForAudioMutex);
if(!trigger) {
if(!waitForAudio.timed_wait(lock, duration)) {
//Condition timed out -- assume audio stream failed
break;
}
}
trigger= false;
audioBufferMutex.lock();
//VISUALIZE AUDIO HERE
//JUST MAKE SURE TO FINISH BEFORE PORTAUDIO MAKES ANOTHER CALLBACK
audioBufferMutex.unlock();
}
//STOP AND CLOSE PORTAUDIO STEAM
Pa_Terminate();
return 0;
}
代替SetEvent(eventVar)
和condition::notify_one()
而不是WaitForSingleObject(eventVar, duration)
。
答案 2 :(得分:0)
您在第一次迭代中关闭了流err = Pa_CloseStream(stream);
。在第二次迭代中,频道已经关闭。在所有迭代之后尝试操作err = Pa_CloseStream(stream);
。
答案 3 :(得分:0)
我这样解决了:
PaStreamParameters inputParameters ,outputParameters;
PaStream* stream;
PaError err;
paTestData data;
int i;
int totalFrames;
int numSamples;
int numBytes;
err = paNoError;
inputParameters.device = 4;
data.maxFrameIndex = totalFrames = NUM_SECONDS * SAMPLE_RATE;
data.frameIndex = 0;
numSamples = totalFrames * NUM_CHANNELS;
numBytes = numSamples * sizeof(SAMPLE);
data.recordedSamples = (SAMPLE *) malloc( numBytes );
std::ofstream arch;
arch.open("signal.csv");
err = Pa_Initialize();
inputParameters.channelCount = 1;
inputParameters.sampleFormat = PA_SAMPLE_TYPE;
inputParameters.suggestedLatency = Pa_GetDeviceInfo( inputParameters.device )->defaultLowInputLatency;
inputParameters.hostApiSpecificStreamInfo = NULL;
int contador = 0;
bool strec = true;
while (strec)
{
err = Pa_OpenStream(
&stream,
&inputParameters,
NULL,
SAMPLE_RATE,
FRAMES_PER_BUFFER,
paClipOff,
recordCallback,
&data );
err = Pa_StartStream( stream );
printf("\n===Grabando.... ===\n"); fflush(stdout);
Pa_Sleep(3000);
while( ( err = Pa_IsStreamActive(stream) ) == 1 )
{
}
err = Pa_CloseStream( stream );
for( i=0; i<numSamples; i++ )
{
val = data.recordedSamples[i];
arch << val << std::endl;
std::cout << std::endl << "valor : " << val;
}
data.frameIndex = 0;
contador++;
if (contador >= 100) //if you delete this condition continuously recorded this audio
{
strec = false;
arch.close();
}
}