草庐IT

ios - 核心音频离线渲染 GenericOutput

coder 2023-07-29 原文

有人成功地使用 core-Audio 完成了离线渲染吗?

我必须混合两个音频文件并应用混响(使用 2 个 AudioFilePlayer、MultiChannelMixer、Reverb2 和 RemoteIO)。 得到它的工作。我可以在预览时保存它(在 RemoteIO 的 renderCallBack 上)。

我需要保存它而不播放它(离线)。 提前致谢。

最佳答案

离线渲染使用 GenericOutput AudioUnit 为我工作。 我在这里分享工作代码。 核心音频框架似乎有点。但是其中的小东西,如 ASBD、参数……等正在制造这些问题。努力它会工作。不要放弃 :-)。 core-audio 在处理低级音频时非常强大和有用。这就是我从过去几周中学到的东西。享受 :-D ....

在.h中声明这些

//AUGraph
AUGraph mGraph;
//Audio Unit References
AudioUnit mFilePlayer;
AudioUnit mFilePlayer2;
AudioUnit mReverb;
AudioUnit mTone;
AudioUnit mMixer;
AudioUnit mGIO;
//Audio File Location
AudioFileID inputFile;
AudioFileID inputFile2;
//Audio file refereces for saving
ExtAudioFileRef extAudioFile;
//Standard sample rate
Float64 graphSampleRate;
AudioStreamBasicDescription stereoStreamFormat864;

Float64 MaxSampleTime;

//在.m类中

- (id) init
{
    self = [super init];
    graphSampleRate = 44100.0;
    MaxSampleTime   = 0.0;
    UInt32 category = kAudioSessionCategory_MediaPlayback;
    CheckError(AudioSessionSetProperty(kAudioSessionProperty_AudioCategory,
                                   sizeof(category),
                                   &category),
           "Couldn't set category on audio session");
    [self initializeAUGraph];
    return self;
}

//ASBD设置

- (void) setupStereoStream864 {    
    // The AudioUnitSampleType data type is the recommended type for sample data in audio
    // units. This obtains the byte size of the type for use in filling in the ASBD.
    size_t bytesPerSample = sizeof (AudioUnitSampleType);
    // Fill the application audio format struct's fields to define a linear PCM,
    // stereo, noninterleaved stream at the hardware sample rate.
    stereoStreamFormat864.mFormatID          = kAudioFormatLinearPCM;
    stereoStreamFormat864.mFormatFlags       = kAudioFormatFlagsAudioUnitCanonical;
    stereoStreamFormat864.mBytesPerPacket    = bytesPerSample;
    stereoStreamFormat864.mFramesPerPacket   = 1;
    stereoStreamFormat864.mBytesPerFrame     = bytesPerSample;
    stereoStreamFormat864.mChannelsPerFrame  = 2; // 2 indicates stereo
    stereoStreamFormat864.mBitsPerChannel    = 8 * bytesPerSample;
    stereoStreamFormat864.mSampleRate        = graphSampleRate;
}

//AUGraph设置

- (void)initializeAUGraph
{
    [self setupStereoStream864];

    // Setup the AUGraph, add AUNodes, and make connections
// create a new AUGraph
CheckError(NewAUGraph(&mGraph),"Couldn't create new graph");

// AUNodes represent AudioUnits on the AUGraph and provide an
// easy means for connecting audioUnits together.
    AUNode filePlayerNode;
    AUNode filePlayerNode2;
AUNode mixerNode;
AUNode reverbNode;
AUNode toneNode;
AUNode gOutputNode;

// file player component
    AudioComponentDescription filePlayer_desc;
filePlayer_desc.componentType = kAudioUnitType_Generator;
filePlayer_desc.componentSubType = kAudioUnitSubType_AudioFilePlayer;
filePlayer_desc.componentFlags = 0;
filePlayer_desc.componentFlagsMask = 0;
filePlayer_desc.componentManufacturer = kAudioUnitManufacturer_Apple;

// file player component2
    AudioComponentDescription filePlayer2_desc;
filePlayer2_desc.componentType = kAudioUnitType_Generator;
filePlayer2_desc.componentSubType = kAudioUnitSubType_AudioFilePlayer;
filePlayer2_desc.componentFlags = 0;
filePlayer2_desc.componentFlagsMask = 0;
filePlayer2_desc.componentManufacturer = kAudioUnitManufacturer_Apple;

// Create AudioComponentDescriptions for the AUs we want in the graph
// mixer component
AudioComponentDescription mixer_desc;
mixer_desc.componentType = kAudioUnitType_Mixer;
mixer_desc.componentSubType = kAudioUnitSubType_MultiChannelMixer;
mixer_desc.componentFlags = 0;
mixer_desc.componentFlagsMask = 0;
mixer_desc.componentManufacturer = kAudioUnitManufacturer_Apple;

// Create AudioComponentDescriptions for the AUs we want in the graph
// Reverb component
AudioComponentDescription reverb_desc;
reverb_desc.componentType = kAudioUnitType_Effect;
reverb_desc.componentSubType = kAudioUnitSubType_Reverb2;
reverb_desc.componentFlags = 0;
reverb_desc.componentFlagsMask = 0;
reverb_desc.componentManufacturer = kAudioUnitManufacturer_Apple;


//tone component
    AudioComponentDescription tone_desc;
tone_desc.componentType = kAudioUnitType_FormatConverter;
//tone_desc.componentSubType = kAudioUnitSubType_NewTimePitch;
    tone_desc.componentSubType = kAudioUnitSubType_Varispeed;
tone_desc.componentFlags = 0;
tone_desc.componentFlagsMask = 0;
tone_desc.componentManufacturer = kAudioUnitManufacturer_Apple;


    AudioComponentDescription gOutput_desc;
gOutput_desc.componentType = kAudioUnitType_Output;
gOutput_desc.componentSubType = kAudioUnitSubType_GenericOutput;
gOutput_desc.componentFlags = 0;
gOutput_desc.componentFlagsMask = 0;
gOutput_desc.componentManufacturer = kAudioUnitManufacturer_Apple;

//Add nodes to graph

// Add nodes to the graph to hold our AudioUnits,
// You pass in a reference to the  AudioComponentDescription
// and get back an  AudioUnit
    AUGraphAddNode(mGraph, &filePlayer_desc, &filePlayerNode );
    AUGraphAddNode(mGraph, &filePlayer2_desc, &filePlayerNode2 );
    AUGraphAddNode(mGraph, &mixer_desc, &mixerNode );
    AUGraphAddNode(mGraph, &reverb_desc, &reverbNode );
    AUGraphAddNode(mGraph, &tone_desc, &toneNode );
AUGraphAddNode(mGraph, &gOutput_desc, &gOutputNode);


//Open the graph early, initialize late
// open the graph AudioUnits are open but not initialized (no resource allocation occurs here)

CheckError(AUGraphOpen(mGraph),"Couldn't Open the graph");

//Reference to Nodes
// get the reference to the AudioUnit object for the file player graph node
AUGraphNodeInfo(mGraph, filePlayerNode, NULL, &mFilePlayer);
AUGraphNodeInfo(mGraph, filePlayerNode2, NULL, &mFilePlayer2);
    AUGraphNodeInfo(mGraph, reverbNode, NULL, &mReverb);
    AUGraphNodeInfo(mGraph, toneNode, NULL, &mTone);
    AUGraphNodeInfo(mGraph, mixerNode, NULL, &mMixer);
AUGraphNodeInfo(mGraph, gOutputNode, NULL, &mGIO);

    AUGraphConnectNodeInput(mGraph, filePlayerNode, 0, reverbNode, 0);
    AUGraphConnectNodeInput(mGraph, reverbNode, 0, toneNode, 0);
    AUGraphConnectNodeInput(mGraph, toneNode, 0, mixerNode,0);
    AUGraphConnectNodeInput(mGraph, filePlayerNode2, 0, mixerNode, 1);
AUGraphConnectNodeInput(mGraph, mixerNode, 0, gOutputNode, 0);


    UInt32 busCount   = 2;    // bus count for mixer unit input

//Setup mixer unit bus count
    CheckError(AudioUnitSetProperty (
                                 mMixer,
                                 kAudioUnitProperty_ElementCount,
                                 kAudioUnitScope_Input,
                                 0,
                                 &busCount,
                                 sizeof (busCount)
                                 ),
           "Couldn't set mixer unit's bus count");

//Enable metering mode to view levels input and output levels of mixer
    UInt32 onValue = 1;
    CheckError(AudioUnitSetProperty(mMixer,
                                kAudioUnitProperty_MeteringMode,
                                kAudioUnitScope_Input,
                                0,
                                &onValue,
                                sizeof(onValue)),
           "error");

// Increase the maximum frames per slice allows the mixer unit to accommodate the
//    larger slice size used when the screen is locked.
    UInt32 maximumFramesPerSlice = 4096;

    CheckError(AudioUnitSetProperty (
                                 mMixer,
                                 kAudioUnitProperty_MaximumFramesPerSlice,
                                 kAudioUnitScope_Global,
                                 0,
                                 &maximumFramesPerSlice,
                                 sizeof (maximumFramesPerSlice)
                                 ),
           "Couldn't set mixer units maximum framers per slice");

// set the audio data format of tone Unit
    AudioUnitSetProperty(mTone,
                     kAudioUnitProperty_StreamFormat,
                     kAudioUnitScope_Global,
                     0,
                     &stereoStreamFormat864,
                     sizeof(AudioStreamBasicDescription));
// set the audio data format of reverb Unit
    AudioUnitSetProperty(mReverb,
                     kAudioUnitProperty_StreamFormat,
                     kAudioUnitScope_Global,
                     0,
                     &stereoStreamFormat864,
                     sizeof(AudioStreamBasicDescription));

// set initial reverb
    AudioUnitParameterValue reverbTime = 2.5;
    AudioUnitSetParameter(mReverb, 4, kAudioUnitScope_Global, 0, reverbTime, 0);
    AudioUnitSetParameter(mReverb, 5, kAudioUnitScope_Global, 0, reverbTime, 0);
    AudioUnitSetParameter(mReverb, 0, kAudioUnitScope_Global, 0, 0, 0);

    AudioStreamBasicDescription     auEffectStreamFormat;
    UInt32 asbdSize = sizeof (auEffectStreamFormat);
memset (&auEffectStreamFormat, 0, sizeof (auEffectStreamFormat ));

// get the audio data format from reverb
CheckError(AudioUnitGetProperty(mReverb,
                                kAudioUnitProperty_StreamFormat,
                                kAudioUnitScope_Input,
                                0,
                                &auEffectStreamFormat,
                                &asbdSize),
           "Couldn't get aueffectunit ASBD");


    auEffectStreamFormat.mSampleRate = graphSampleRate;

// set the audio data format of mixer Unit
    CheckError(AudioUnitSetProperty(mMixer,
                                kAudioUnitProperty_StreamFormat,
                                kAudioUnitScope_Output,
                                0,
                                &auEffectStreamFormat, sizeof(auEffectStreamFormat)),
           "Couldn't set ASBD on mixer output");

CheckError(AUGraphInitialize(mGraph),"Couldn't Initialize the graph");

    [self setUpAUFilePlayer];
    [self setUpAUFilePlayer2];  
}

//Audio file playback setup 这里我设置的是语音文件

-(OSStatus) setUpAUFilePlayer{
NSString *songPath = [[NSBundle mainBundle] pathForResource: @"testVoice" ofType:@".m4a"];
CFURLRef songURL = ( CFURLRef) [NSURL fileURLWithPath:songPath];

// open the input audio file
CheckError(AudioFileOpenURL(songURL, kAudioFileReadPermission, 0, &inputFile),
           "setUpAUFilePlayer AudioFileOpenURL failed");

AudioStreamBasicDescription fileASBD;
// get the audio data format from the file
UInt32 propSize = sizeof(fileASBD);
CheckError(AudioFileGetProperty(inputFile, kAudioFilePropertyDataFormat,
                                &propSize, &fileASBD),
           "setUpAUFilePlayer couldn't get file's data format");

// tell the file player unit to load the file we want to play
CheckError(AudioUnitSetProperty(mFilePlayer, kAudioUnitProperty_ScheduledFileIDs,
                                kAudioUnitScope_Global, 0, &inputFile, sizeof(inputFile)),
           "setUpAUFilePlayer AudioUnitSetProperty[kAudioUnitProperty_ScheduledFileIDs] failed");

UInt64 nPackets;
UInt32 propsize = sizeof(nPackets);
CheckError(AudioFileGetProperty(inputFile, kAudioFilePropertyAudioDataPacketCount,
                                &propsize, &nPackets),
           "setUpAUFilePlayer AudioFileGetProperty[kAudioFilePropertyAudioDataPacketCount] failed");

// tell the file player AU to play the entire file
ScheduledAudioFileRegion rgn;
memset (&rgn.mTimeStamp, 0, sizeof(rgn.mTimeStamp));
rgn.mTimeStamp.mFlags = kAudioTimeStampSampleTimeValid;
rgn.mTimeStamp.mSampleTime = 0;
rgn.mCompletionProc = NULL;
rgn.mCompletionProcUserData = NULL;
rgn.mAudioFile = inputFile;
rgn.mLoopCount = -1;
rgn.mStartFrame = 0;
rgn.mFramesToPlay = nPackets * fileASBD.mFramesPerPacket;

if (MaxSampleTime < rgn.mFramesToPlay)
{
    MaxSampleTime = rgn.mFramesToPlay;
}

CheckError(AudioUnitSetProperty(mFilePlayer, kAudioUnitProperty_ScheduledFileRegion,
                                kAudioUnitScope_Global, 0,&rgn, sizeof(rgn)),
           "setUpAUFilePlayer1 AudioUnitSetProperty[kAudioUnitProperty_ScheduledFileRegion] failed");

// prime the file player AU with default values
UInt32 defaultVal = 0;

CheckError(AudioUnitSetProperty(mFilePlayer, kAudioUnitProperty_ScheduledFilePrime,
                                kAudioUnitScope_Global, 0, &defaultVal, sizeof(defaultVal)),
           "setUpAUFilePlayer AudioUnitSetProperty[kAudioUnitProperty_ScheduledFilePrime] failed");


// tell the file player AU when to start playing (-1 sample time means next render cycle)
AudioTimeStamp startTime;
memset (&startTime, 0, sizeof(startTime));
startTime.mFlags = kAudioTimeStampSampleTimeValid;

startTime.mSampleTime = -1;
CheckError(AudioUnitSetProperty(mFilePlayer, kAudioUnitProperty_ScheduleStartTimeStamp,
                                kAudioUnitScope_Global, 0, &startTime, sizeof(startTime)),
           "setUpAUFilePlayer AudioUnitSetProperty[kAudioUnitProperty_ScheduleStartTimeStamp]");

return noErr;  
}

//音频文件播放设置这里我设置的是BGMusic文件

-(OSStatus) setUpAUFilePlayer2{
NSString *songPath = [[NSBundle mainBundle] pathForResource: @"BGmusic" ofType:@".mp3"];
CFURLRef songURL = ( CFURLRef) [NSURL fileURLWithPath:songPath];

// open the input audio file
CheckError(AudioFileOpenURL(songURL, kAudioFileReadPermission, 0, &inputFile2),
           "setUpAUFilePlayer2 AudioFileOpenURL failed");

AudioStreamBasicDescription fileASBD;
// get the audio data format from the file
UInt32 propSize = sizeof(fileASBD);
CheckError(AudioFileGetProperty(inputFile2, kAudioFilePropertyDataFormat,
                                &propSize, &fileASBD),
           "setUpAUFilePlayer2 couldn't get file's data format");

// tell the file player unit to load the file we want to play
CheckError(AudioUnitSetProperty(mFilePlayer2, kAudioUnitProperty_ScheduledFileIDs,
                                kAudioUnitScope_Global, 0, &inputFile2, sizeof(inputFile2)),
           "setUpAUFilePlayer2 AudioUnitSetProperty[kAudioUnitProperty_ScheduledFileIDs] failed");

UInt64 nPackets;
UInt32 propsize = sizeof(nPackets);
CheckError(AudioFileGetProperty(inputFile2, kAudioFilePropertyAudioDataPacketCount,
                                &propsize, &nPackets),
           "setUpAUFilePlayer2 AudioFileGetProperty[kAudioFilePropertyAudioDataPacketCount] failed");

// tell the file player AU to play the entire file
ScheduledAudioFileRegion rgn;
memset (&rgn.mTimeStamp, 0, sizeof(rgn.mTimeStamp));
rgn.mTimeStamp.mFlags = kAudioTimeStampSampleTimeValid;
rgn.mTimeStamp.mSampleTime = 0;
rgn.mCompletionProc = NULL;
rgn.mCompletionProcUserData = NULL;
rgn.mAudioFile = inputFile2;
rgn.mLoopCount = -1;
rgn.mStartFrame = 0;
rgn.mFramesToPlay = nPackets * fileASBD.mFramesPerPacket;


if (MaxSampleTime < rgn.mFramesToPlay)
{
    MaxSampleTime = rgn.mFramesToPlay;
}

CheckError(AudioUnitSetProperty(mFilePlayer2, kAudioUnitProperty_ScheduledFileRegion,
                                kAudioUnitScope_Global, 0,&rgn, sizeof(rgn)),
           "setUpAUFilePlayer2 AudioUnitSetProperty[kAudioUnitProperty_ScheduledFileRegion] failed");

// prime the file player AU with default values
UInt32 defaultVal = 0;
CheckError(AudioUnitSetProperty(mFilePlayer2, kAudioUnitProperty_ScheduledFilePrime,
                                kAudioUnitScope_Global, 0, &defaultVal, sizeof(defaultVal)),
           "setUpAUFilePlayer2 AudioUnitSetProperty[kAudioUnitProperty_ScheduledFilePrime] failed");


// tell the file player AU when to start playing (-1 sample time means next render cycle)
AudioTimeStamp startTime;
memset (&startTime, 0, sizeof(startTime));
startTime.mFlags = kAudioTimeStampSampleTimeValid;
startTime.mSampleTime = -1;
CheckError(AudioUnitSetProperty(mFilePlayer2, kAudioUnitProperty_ScheduleStartTimeStamp,
                                kAudioUnitScope_Global, 0, &startTime, sizeof(startTime)),
           "setUpAUFilePlayer2 AudioUnitSetProperty[kAudioUnitProperty_ScheduleStartTimeStamp]");

return noErr;  
}

//开始保存文件

- (void)startRecordingAAC{
AudioStreamBasicDescription destinationFormat;
memset(&destinationFormat, 0, sizeof(destinationFormat));
destinationFormat.mChannelsPerFrame = 2;
destinationFormat.mFormatID = kAudioFormatMPEG4AAC;
UInt32 size = sizeof(destinationFormat);
OSStatus result = AudioFormatGetProperty(kAudioFormatProperty_FormatInfo, 0, NULL, &size, &destinationFormat);
if(result) printf("AudioFormatGetProperty %ld \n", result);
NSArray  *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];



NSString *destinationFilePath = [[NSString alloc] initWithFormat: @"%@/output.m4a", documentsDirectory];
CFURLRef destinationURL = CFURLCreateWithFileSystemPath(kCFAllocatorDefault,
                                                        (CFStringRef)destinationFilePath,
                                                        kCFURLPOSIXPathStyle,
                                                        false);
[destinationFilePath release];

// specify codec Saving the output in .m4a format
result = ExtAudioFileCreateWithURL(destinationURL,
                                   kAudioFileM4AType,
                                   &destinationFormat,
                                   NULL,
                                   kAudioFileFlags_EraseFile,
                                   &extAudioFile);
if(result) printf("ExtAudioFileCreateWithURL %ld \n", result);
CFRelease(destinationURL);

// This is a very important part and easiest way to set the ASBD for the File with correct format.
AudioStreamBasicDescription clientFormat;
UInt32 fSize = sizeof (clientFormat);
memset(&clientFormat, 0, sizeof(clientFormat));
// get the audio data format from the Output Unit
CheckError(AudioUnitGetProperty(mGIO,
                                kAudioUnitProperty_StreamFormat,
                                kAudioUnitScope_Output,
                                0,
                                &clientFormat,
                                &fSize),"AudioUnitGetProperty on failed");

// set the audio data format of mixer Unit
CheckError(ExtAudioFileSetProperty(extAudioFile,
                                   kExtAudioFileProperty_ClientDataFormat,
                                   sizeof(clientFormat),
                                   &clientFormat),
           "ExtAudioFileSetProperty kExtAudioFileProperty_ClientDataFormat failed");
// specify codec
UInt32 codec = kAppleHardwareAudioCodecManufacturer;
CheckError(ExtAudioFileSetProperty(extAudioFile,
                                   kExtAudioFileProperty_CodecManufacturer,
                                   sizeof(codec),
                                   &codec),"ExtAudioFileSetProperty on extAudioFile Faild");

CheckError(ExtAudioFileWriteAsync(extAudioFile, 0, NULL),"ExtAudioFileWriteAsync Failed");

[self pullGenericOutput];
}

//手动输入并从 GenericOutput 节点获取数据/缓冲区。

-(void)pullGenericOutput{
AudioUnitRenderActionFlags flags = 0;
AudioTimeStamp inTimeStamp;
memset(&inTimeStamp, 0, sizeof(AudioTimeStamp));
inTimeStamp.mFlags = kAudioTimeStampSampleTimeValid;
UInt32 busNumber = 0;
UInt32 numberFrames = 512;
inTimeStamp.mSampleTime = 0;
int channelCount = 2;

NSLog(@"Final numberFrames :%li",numberFrames);
int totFrms = MaxSampleTime;
while (totFrms > 0)
{
    if (totFrms < numberFrames)
    {
        numberFrames = totFrms;
        NSLog(@"Final numberFrames :%li",numberFrames);
    }
    else
    {
        totFrms -= numberFrames;
    }
    AudioBufferList *bufferList = (AudioBufferList*)malloc(sizeof(AudioBufferList)+sizeof(AudioBuffer)*(channelCount-1));
    bufferList->mNumberBuffers = channelCount;
    for (int j=0; j<channelCount; j++)
    {
        AudioBuffer buffer = {0};
        buffer.mNumberChannels = 1;
        buffer.mDataByteSize = numberFrames*sizeof(AudioUnitSampleType);
        buffer.mData = calloc(numberFrames, sizeof(AudioUnitSampleType));

        bufferList->mBuffers[j] = buffer;

    }
    CheckError(AudioUnitRender(mGIO,
                               &flags,
                               &inTimeStamp,
                               busNumber,
                               numberFrames,
                               bufferList),
               "AudioUnitRender mGIO");



    CheckError(ExtAudioFileWrite(extAudioFile, numberFrames, bufferList),("extaudiofilewrite fail"));

}

[self FilesSavingCompleted];
}

//文件保存完成

-(void)FilesSavingCompleted{
OSStatus status = ExtAudioFileDispose(extAudioFile);
printf("OSStatus(ExtAudioFileDispose): %ld\n", status);
}

关于ios - 核心音频离线渲染 GenericOutput,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/15297990/

有关ios - 核心音频离线渲染 GenericOutput的更多相关文章

  1. ruby-on-rails - 渲染另一个 Controller 的 View - 2

    我想要做的是有2个不同的Controller,client和test_client。客户端Controller已经构建,我想创建一个test_clientController,我可以使用它来玩弄客户端的UI并根据需要进行调整。我主要是想绕过我在客户端中内置的验证及其对加载数据的管理Controller的依赖。所以我希望test_clientController加载示例数据集,然后呈现客户端Controller的索引View,以便我可以调整客户端UI。就是这样。我在test_clients索引方法中试过这个:classTestClientdefindexrender:template=>

  2. ruby - 完全离线安装RVM - 2

    我打算为ruby​​脚本创建一个安装程序,但我希望能够确保机器安装了RVM。有没有一种方法可以完全离线安装RVM并且不引人注目(通过不引人注目,就像创建一个可以做所有事情的脚本而不是要求用户向他们的bash_profile或bashrc添加一些东西)我不是要脚本本身,只是一个关于如何走这条路的快速指针(如果可能的话)。我们还研究了这个很有帮助的问题:RVM-isthereawayforsimpleofflineinstall?但有点误导,因为答案只向我们展示了如何离线在RVM中安装ruby。我们需要能够离线安装RVM本身,并查看脚本https://raw.github.com/wayn

  3. ruby-on-rails - Rails HTML 请求渲染 JSON - 2

    在我的Controller中,我通过以下方式在我的index方法中支持HTML和JSON:respond_todo|format|format.htmlformat.json{renderjson:@user}end在浏览器中拉起它时,它会自然地以HTML呈现。但是,当我对/user资源进行内容类型为application/json的curl调用时(因为它是索引方法),我仍然将HTML作为响应。如何获取JSON作为响应?我还需要说明什么? 最佳答案 您应该将.json附加到请求的url,提供的格式在routes.rb的路径中定义。这

  4. ruby - 如何验证 IO.copy_stream 是否成功 - 2

    这里有一个很好的答案解释了如何在Ruby中下载文件而不将其加载到内存中:https://stackoverflow.com/a/29743394/4852737require'open-uri'download=open('http://example.com/image.png')IO.copy_stream(download,'~/image.png')我如何验证下载文件的IO.copy_stream调用是否真的成功——这意味着下载的文件与我打算下载的文件完全相同,而不是下载一半的损坏文件?documentation说IO.copy_stream返回它复制的字节数,但是当我还没有下

  5. Ruby 文件 IO 定界符? - 2

    我正在尝试解析一个文本文件,该文件每行包含可变数量的单词和数字,如下所示:foo4.500bar3.001.33foobar如何读取由空格而不是换行符分隔的文件?有什么方法可以设置File("file.txt").foreach方法以使用空格而不是换行符作为分隔符? 最佳答案 接受的答案将slurp文件,这可能是大文本文件的问题。更好的解决方案是IO.foreach.它是惯用的,将按字符流式传输文件:File.foreach(filename,""){|string|putsstring}包含“thisisanexample”结果的

  6. 世界前沿3D开发引擎HOOPS全面讲解——集3D数据读取、3D图形渲染、3D数据发布于一体的全新3D应用开发工具 - 2

    无论您是想搭建桌面端、WEB端或者移动端APP应用,HOOPSPlatform组件都可以为您提供弹性的3D集成架构,同时,由工业领域3D技术专家组成的HOOPS技术团队也能为您提供技术支持服务。如果您的客户期望有一种在多个平台(桌面/WEB/APP,而且某些客户端是“瘦”客户端)快速、方便地将数据接入到3D应用系统的解决方案,并且当访问数据时,在各个平台上的性能和用户体验保持一致,HOOPSPlatform将帮助您完成。利用HOOPSPlatform,您可以开发在任何环境下的3D基础应用架构。HOOPSPlatform可以帮您打造3D创新型产品,HOOPSSDK包含的技术有:快速且准确的CAD

  7. Get https://registry-1.docker.io/v2/: net/http: request canceled while waiting - 2

    1.错误信息:Errorresponsefromdaemon:Gethttps://registry-1.docker.io/v2/:net/http:requestcanceledwhilewaitingforconnection(Client.Timeoutexceededwhileawaitingheaders)或者:Errorresponsefromdaemon:Gethttps://registry-1.docker.io/v2/:net/http:TLShandshaketimeout2.报错原因:docker使用的镜像网址默认为国外,下载容易超时,需要修改成国内镜像地址(首先阿里

  8. ruby - 为什么不能使用类IO的实例方法noecho? - 2

    print"Enteryourpassword:"pass=STDIN.noecho(&:gets)puts"Yourpasswordis#{pass}!"输出:Enteryourpassword:input.rb:2:in`':undefinedmethod`noecho'for#>(NoMethodError) 最佳答案 一开始require'io/console'后来的Ruby1.9.3 关于ruby-为什么不能使用类IO的实例方法noecho?,我们在StackOverflow上

  9. ruby-on-rails - Rails 渲染带有驼峰命名法的 json 对象 - 2

    我在一个简单的RailsAPI中有以下Controller代码:classApi::V1::AccountsControllerehead:not_foundendendend问题在于,生成的json具有以下格式:{id:2,name:'Simpleaccount',cash_flows:[{id:1,amount:34.3,description:'simpledescription'},{id:2,amount:1.12,description:'otherdescription'}]}我需要我生成的json是camelCase('cashFlows'而不是'cash_flows'

  10. ruby-on-rails - 使用 header 渲染 JSON - 2

    我想在我的Controller中使用以下corsheader呈现JSON:'Access-Control-Allow-Origin'='*'.我试过这个:defmy_actionrender(json:some_params)response.headers['Access-Control-Allow-Origin']='*'end但是我得到了一个AbstractController::DoubleRenderError。有没有办法使用header呈现JSON? 最佳答案 您不能在渲染后设置header,因为已发送响应。所以在没有意

随机推荐