I'm trying to create an .ogg file, but even the example code presented generates an Access Violation when calling vorbis_analysis().
I'm using a simplified version of this encoding Example. I have tried the full example as it is, but I get the same error at the same place.
The code below is the example simplified by filling the encoding buffer with a constant instead of a wav file. The hFile is a TStreamFile instance created elsewhere.
vorbis_info vi;
vorbis_dsp_state vd;
vorbis_comment vc;
vorbis_block vb;
ogg_stream_state os;
ogg_page og;
ogg_packet op;
ogg_packet header;
ogg_packet header_comm;
ogg_packet header_code;
int res;
int len = 0;
int eos = 0;
int loop = 0;
vorbis_info_init(&vi);
res = vorbis_encode_init_vbr(&vi,2,44100,0.4f);
res = vorbis_encode_setup_init(&vi);
vorbis_comment_init(&vc);
vorbis_comment_add_tag(&vc,"TITLE","Silent noise");
res = vorbis_analysis_init(&vd,&vi);
res = vorbis_block_init(&vd,&vb);
srand(time(NULL));
res = ogg_stream_init(&os,rand());
res = vorbis_analysis_headerout(&vd,&vc,&header,&header_comm,&header_code);
res = ogg_stream_packetin(&os,&header);
res = ogg_stream_packetin(&os,&header_comm);
res = ogg_stream_packetin(&os,&header_code);
/* This ensures the actual
* audio data will start on a new page, as per spec
*/
while(ogg_stream_flush(&os,&og)) {
hFile->Write(og.header,og.header_len);
hFile->Write(og.body, og.body_len );
}
while(!eos) {
float **Buffer = vorbis_analysis_buffer(&vd,1024);
if(loop<10) {
for(int n=0; n<1024; n++) {
Buffer[0][n] = 1.0f;
Buffer[1][n] = 1.0f;
}
res = vorbis_analysis_wrote(&vd,1024);
loop++;
} else {
res = vorbis_analysis_wrote(&vd,0);
eos = true;
}
// Enough data?
while(vorbis_analysis_blockout(&vd,&vb) == 1) {
res = vorbis_analysis(&vb,&op); // <---- Access Violation
res = vorbis_bitrate_addblock(&vb);
while(vorbis_bitrate_flushpacket(&vd,&op)) {
/* weld the packet into the bitstream */
res = ogg_stream_packetin(&os,&op);
/* write out pages (if any) */
while(!eos){
if(ogg_stream_pageout(&os,&og) == 0) {
break;
}
hFile->Write(og.header,og.header_len);
hFile->Write(og.body, og.body_len );
/* this could be set above, but for illustrative purposes, I do
it here (to show that vorbis does know where the stream ends) */
if(ogg_page_eos(&og)) {
eos=1;
}
}
}
}
}
res = ogg_stream_clear(&os);
res = vorbis_block_clear(&vb);
vorbis_dsp_clear(&vd);
vorbis_comment_clear(&vc);
vorbis_info_clear(&vi);
The first part runs fine, the header is created and saved to the stream. Then I just fill the buffer with 1.0 floats for test purposes.
When there is enough data available the call to vorbis_analysis always generate Access Violation at x write of address x
The variable res always indicate success all the way down to the access violation.
I'm using static linked libogg v1.3.2 and libvorbis 1.3.5
Related
I am running into some problems trying to create a ProRes encoded mov file using the AVFramework framework, and AVAsset.
On OSX 10.10.5, using XCode 7, linking against 10.9 libraries.
So far I have managed to create valid ProRes files that contain both video and multiple channels of audio.
( I am creating multiple tracks of uncompressed 48K, 16-bit PCM Audio)
Adding the Video Frames work well, and adding the Audio frames works well, or at least succeeds in the code.
However when i play the file back, it appears as though the audio frames are repeated, in 12,13,14, or 15 frame sequences.
Looking at the wave form, from the *.mov it is easy to see the repeated audio...
That is to say, the first 13 or X video frames all contain exactly the same audio, this is then again repeated for the next X, and then again and again and again etc...
The Video is fine, it is just the Audio that appears to be looping/repeating.
The issue appears no matter how many audio channels/ tracks I use as the source, I have tested using just 1 track and also using 4 and 8 tracks.
It is independent of what format and amount of samples i feed to the system, ie using, 720p60, 1080p23, and 1080i59 all exhibit the same incorrect behavior.
well actually the 720p captures appears to repeat the audio frames 30 or 31 times, and the 1080 formats only repeat the audio frames 12 or 13 times,
But i am definitely submitting different audio data to the Audio encode/SampleBuffer create process, as i have logged this in great detail ( tho it is not shown in the code below)
I have tried a number of different things to modify the code and expose the issue, but had no success, hence i am asking here, and hopefully someone can either see an issue with my code or give me some info with regards to this problem.
The code i am using is as follows:
int main(int argc, const char * argv[])
{
#autoreleasepool
{
NSLog(#"Hello, World! - Welcome to the ProResCapture With Audio sample app. ");
OSStatus status;
AudioStreamBasicDescription audioFormat;
CMAudioFormatDescriptionRef audioFormatDesc;
// OK so lets include the hardware stuff first and then we can see about doing some actual capture and compress stuff
HARDWARE_HANDLE pHardware = sdiFactory();
if (pHardware)
{
unsigned long ulUpdateType = UPD_FMT_FRAME;
unsigned long ulFieldCount = 0;
unsigned int numAudioChannels = 4; //8; //4;
int numFramesToCapture = 300;
gBFHancBuffer = (unsigned int*)myAlloc(gHANC_SIZE);
int audioSize = 2002 * 4 * 16;
short* pAudioSamples = (short*)new char[audioSize];
std::vector<short*> vecOfNonInterleavedAudioSamplesPtrs;
for (int i = 0; i < 16; i++)
{
vecOfNonInterleavedAudioSamplesPtrs.push_back((short*)myAlloc(2002 * sizeof(short)));
}
bool bVideoModeIsValid = SetupAndConfigureHardwareToCaptureIncomingVideo();
if (bVideoModeIsValid)
{
gBFBytes = (BLUE_UINT32*)myAlloc(gGoldenSize);
bool canAddVideoWriter = false;
bool canAddAudioWriter = false;
int nAudioSamplesWritten = 0;
// declare the vars for our various AVAsset elements
AVAssetWriter* assetWriter = nil;
AVAssetWriterInput* assetWriterInputVideo = nil;
AVAssetWriterInput* assetWriterAudioInput[16];
AVAssetWriterInputPixelBufferAdaptor* adaptor = nil;
NSURL* localOutputURL = nil;
NSError* localError = nil;
// create the file we are goijmng to be writing to
localOutputURL = [NSURL URLWithString:#"file:///Volumes/Media/ProResAVCaptureAnyFormat.mov"];
assetWriter = [[AVAssetWriter alloc] initWithURL: localOutputURL fileType:AVFileTypeQuickTimeMovie error:&localError];
if (assetWriter)
{
assetWriter.shouldOptimizeForNetworkUse = NO;
// Lets configure the Audio and Video settings for this writer...
{
// Video First.
// Add a video input
// create a dictionary with the settings we want ie. Prores capture and width and height.
NSMutableDictionary* videoSettings = [NSMutableDictionary dictionaryWithObjectsAndKeys:
AVVideoCodecAppleProRes422, AVVideoCodecKey,
[NSNumber numberWithInt:width], AVVideoWidthKey,
[NSNumber numberWithInt:height], AVVideoHeightKey,
nil];
assetWriterInputVideo = [AVAssetWriterInput assetWriterInputWithMediaType: AVMediaTypeVideo outputSettings:videoSettings];
adaptor = [AVAssetWriterInputPixelBufferAdaptor assetWriterInputPixelBufferAdaptorWithAssetWriterInput:assetWriterInputVideo
sourcePixelBufferAttributes:nil];
canAddVideoWriter = [assetWriter canAddInput:assetWriterInputVideo];
}
{ // Add a Audio AssetWriterInput
// Create a dictionary with the settings we want ie. Uncompressed PCM audio 16 bit little endian.
NSMutableDictionary* audioSettings = [NSMutableDictionary dictionaryWithObjectsAndKeys:
[NSNumber numberWithInt:kAudioFormatLinearPCM], AVFormatIDKey,
[NSNumber numberWithFloat:48000.0], AVSampleRateKey,
[NSNumber numberWithInt:16], AVLinearPCMBitDepthKey,
[NSNumber numberWithBool:NO], AVLinearPCMIsNonInterleaved,
[NSNumber numberWithBool:NO], AVLinearPCMIsFloatKey,
[NSNumber numberWithBool:NO], AVLinearPCMIsBigEndianKey,
[NSNumber numberWithUnsignedInteger:1], AVNumberOfChannelsKey,
nil];
// OR use... FillOutASBDForLPCM(AudioStreamBasicDescription& outASBD, Float64 inSampleRate, UInt32 inChannelsPerFrame, UInt32 inValidBitsPerChannel, UInt32 inTotalBitsPerChannel, bool inIsFloat, bool inIsBigEndian, bool inIsNonInterleaved = false)
UInt32 inValidBitsPerChannel = 16;
UInt32 inTotalBitsPerChannel = 16;
bool inIsFloat = false;
bool inIsBigEndian = false;
UInt32 inChannelsPerTrack = 1;
FillOutASBDForLPCM(audioFormat, 48000.00, inChannelsPerTrack, inValidBitsPerChannel, inTotalBitsPerChannel, inIsFloat, inIsBigEndian);
status = CMAudioFormatDescriptionCreate(kCFAllocatorDefault,
&audioFormat,
0,
NULL,
0,
NULL,
NULL,
&audioFormatDesc
);
for (int t = 0; t < numAudioChannels; t++)
{
assetWriterAudioInput[t] = [AVAssetWriterInput assetWriterInputWithMediaType:AVMediaTypeAudio outputSettings:audioSettings];
canAddAudioWriter = [assetWriter canAddInput:assetWriterAudioInput[t] ];
if (canAddAudioWriter)
{
assetWriterAudioInput[t].expectsMediaDataInRealTime = YES; //true;
[assetWriter addInput:assetWriterAudioInput[t] ];
}
}
CMFormatDescriptionRef myFormatDesc = assetWriterAudioInput[0].sourceFormatHint;
NSString* medType = [assetWriterAudioInput[0] mediaType];
}
if(canAddVideoWriter)
{
// tell the asset writer to expect media in real time.
assetWriterInputVideo.expectsMediaDataInRealTime = YES; //true;
// add the Input(s)
[assetWriter addInput:assetWriterInputVideo];
// Start writing the frames..
BOOL success = true;
success = [assetWriter startWriting];
CMTime startTime = CMTimeMake(0, fpsRate);
[assetWriter startSessionAtSourceTime:kCMTimeZero];
// [assetWriter startSessionAtSourceTime:startTime];
if (success)
{
startOurVideoCaptureProcess();
// **** possible enhancement is to use a pixelBufferPool to manage multiple buffers at once...
CVPixelBufferRef buffer = NULL;
int kRecordingFPS = fpsRate;
bool frameAdded = false;
unsigned int bufferID;
for( int i = 0; i < numFramesToCapture; i++)
{
printf("\n");
buffer = pixelBufferFromCard(bufferID, width, height, memFmt); // This function to get a CVBufferREf From our device, as well as getting the Audio data
while(!adaptor.assetWriterInput.readyForMoreMediaData)
{
printf(" readyForMoreMediaData FAILED \n");
}
if (buffer)
{
// Add video
printf("appending Frame %d ", i);
CMTime frameTime = CMTimeMake(i, kRecordingFPS);
frameAdded = [adaptor appendPixelBuffer:buffer withPresentationTime:frameTime];
if (frameAdded)
printf("VideoAdded.....\n ");
// Add Audio
{
// Do some Processing on the captured data to extract the interleaved Audio Samples for each channel
struct hanc_decode_struct decode;
DecodeHancFrameEx(gBFHancBuffer, decode);
int nAudioSamplesCaptured = 0;
if(decode.no_audio_samples > 0)
{
printf("completed deCodeHancEX, found %d samples \n", ( decode.no_audio_samples / numAudioChannels) );
nAudioSamplesCaptured = decode.no_audio_samples / numAudioChannels;
}
CMTime audioTimeStamp = CMTimeMake(nAudioSamplesWritten, 480000); // (Samples Written) / sampleRate for audio
// This function repacks the Audio from interleaved PCM data a vector of individual array of Audio data
RepackDecodedHancAudio((void*)pAudioSamples, numAudioChannels, nAudioSamplesCaptured, vecOfNonInterleavedAudioSamplesPtrs);
for (int t = 0; t < numAudioChannels; t++)
{
CMBlockBufferRef blockBuf = NULL; // *********** MUST release these AFTER adding the samples to the assetWriter...
CMSampleBufferRef cmBuf = NULL;
int sizeOfSamplesInBytes = nAudioSamplesCaptured * 2; // always 16bit memory samples...
// Create sample Block buffer for adding to the audio input.
status = CMBlockBufferCreateWithMemoryBlock(kCFAllocatorDefault,
(void*)vecOfNonInterleavedAudioSamplesPtrs[t],
sizeOfSamplesInBytes,
kCFAllocatorNull,
NULL,
0,
sizeOfSamplesInBytes,
0,
&blockBuf);
if (status != noErr)
NSLog(#"CMBlockBufferCreateWithMemoryBlock error");
status = CMAudioSampleBufferCreateWithPacketDescriptions(kCFAllocatorDefault,
blockBuf,
TRUE,
0,
NULL,
audioFormatDesc,
nAudioSamplesCaptured,
audioTimeStamp,
NULL,
&cmBuf);
if (status != noErr)
NSLog(#"CMSampleBufferCreate error");
// leys check if the CMSampleBuf is valid
bool bValid = CMSampleBufferIsValid(cmBuf);
// examine this values for debugging info....
CMTime cmTimeSampleDuration = CMSampleBufferGetDuration(cmBuf);
CMTime cmTimePresentationTime = CMSampleBufferGetPresentationTimeStamp(cmBuf);
if (status != noErr)
NSLog(#"Invalid Buffer found!!! possible CMSampleBufferCreate error?");
if(!assetWriterAudioInput[t].readyForMoreMediaData)
printf(" readyForMoreMediaData FAILED - Had to Drop a frame\n");
else
{
if(assetWriter.status == AVAssetWriterStatusWriting)
{
BOOL r = YES;
r = [assetWriterAudioInput[t] appendSampleBuffer:cmBuf];
if (!r)
{
NSLog(#"appendSampleBuffer error");
}
else
success = true;
}
else
printf("AssetWriter Not ready???!? \n");
}
if (cmBuf)
{
CFRelease(cmBuf);
cmBuf = 0;
}
if(blockBuf)
{
CFRelease(blockBuf);
blockBuf = 0;
}
}
nAudioSamplesWritten = nAudioSamplesWritten + nAudioSamplesCaptured;
}
if(success)
{
printf("Audio tracks Added..");
}
else
{
NSError* nsERR = [assetWriter error];
printf("Problem Adding Audio tracks / samples");
}
printf("Success \n");
}
if (buffer)
{
CVBufferRelease(buffer);
}
}
}
AVAssetWriterStatus sta = [assetWriter status];
CMTime endTime = CMTimeMake((numFramesToCapture-1), fpsRate);
if (audioFormatDesc)
{
CFRelease(audioFormatDesc);
audioFormatDesc = 0;
}
// Finish the session
StopVideoCaptureProcess();
[assetWriterInputVideo markAsFinished];
for (int t = 0; t < numAudioChannels; t++)
{
[assetWriterAudioInput[t] markAsFinished];
}
[assetWriter endSessionAtSourceTime:endTime];
bool finishedSuccessfully = [assetWriter finishWriting];
if (finishedSuccessfully)
NSLog(#"Writing file ended successfully \n");
else
{
NSLog(#"Writing file ended WITH ERRORS...");
sta = [assetWriter status];
if (sta != AVAssetWriterStatusCompleted)
{
NSError* nsERR = [assetWriter error];
printf("investoigating the error \n");
}
}
}
else
{
NSLog(#"Unable to Add the InputVideo Asset Writer to the AssetWriter, file will not be written - Exiting");
}
if (audioFormatDesc)
CFRelease(audioFormatDesc);
}
for (int i = 0; i < 16; i++)
{
if (vecOfNonInterleavedAudioSamplesPtrs[i])
{
bfFree(2002 * sizeof(unsigned short), vecOfNonInterleavedAudioSamplesPtrs[i]);
vecOfNonInterleavedAudioSamplesPtrs[i] = nullptr;
}
}
}
else
{
NSLog(#"Unable to find a valid input signal - Exiting");
}
if (pAudioSamples)
delete pAudioSamples;
}
}
return 0;
}
It's a very basic sample that connects to some special hardware ( code for that is left out)
It grabs frames of video and audio, and then there is the processing for the Audio to go from interleaved PCM to the individual Array's of PCM data for each track
and then each buffer is added to the appropriate track, be it video or audio...
Lastly the AvAsset stuff is finished and closed and i exit and clean up.
Any help will be most appreciated,
Cheers,
James
Well i finally found a working solution for this problem.
The solution comes in 2 parts:
I moved from using CMAudioSampleBufferCreateWithPacketDescriptions
to using CMSampleBufferCreate(..) and the appropriate arguments to that function call.
Initially when experiementing with CMSampleBufferCreate i was mis-using some of the arguments and it was giving me the same results as i initially outlined here, but with careful examination of the values i was passing for the CMSampleTimingInfo struct - specifically the duration part, i eventually got everything working correctly!!
So it appears that i was creating the CMBlockBufferRef correctly, but i needed to take more care when using this to create the CMSampleBufRef that i was passing to the AVAssetWriterInput!
Hope this helps someone else, as it was a nasty one for me to solve!
James
I have just started with electronics, and doing a project using the Spark Photon, which is based on Arduino. The project website is here: http://hackster.io/middleca/sending-sound-over-the-internet
I uploaded the following two files (.ino and .js) to the Photon, which should then capture and transmit sound (directly I assume). I expected a test.wav would be created. However, where should I find this file so I can check if everything worked?
main.ino file:
#define MICROPHONE_PIN A5
#define AUDIO_BUFFER_MAX 8192
int audioStartIdx = 0, audioEndIdx = 0;
uint16_t audioBuffer[AUDIO_BUFFER_MAX];
uint16_t txBuffer[AUDIO_BUFFER_MAX];
// version without timers
unsigned long lastRead = micros();
char myIpAddress[24];
TCPClient audioClient;
TCPClient checkClient;
TCPServer audioServer = TCPServer(3443);
void setup() {
Serial.begin(115200);
pinMode(MICROPHONE_PIN, INPUT);
// so we know where to connect, try:
// particle get MY_DEVICE_NAME ipAddress
Spark.variable("ipAddress", myIpAddress, STRING);
IPAddress myIp = WiFi.localIP();
sprintf(myIpAddress, "%d.%d.%d.%d", myIp[0], myIp[1], myIp[2], myIp[3]);
// 1/8000th of a second is 125 microseconds
audioServer.begin();
lastRead = micros();
}
void loop() {
checkClient = audioServer.available();
if (checkClient.connected()) {
audioClient = checkClient;
}
//listen for 100ms, taking a sample every 125us,
//and then send that chunk over the network.
listenAndSend(100);
}
void listenAndSend(int delay) {
unsigned long startedListening = millis();
while ((millis() - startedListening) < delay) {
unsigned long time = micros();
if (lastRead > time) {
// time wrapped?
//lets just skip a beat for now, whatever.
lastRead = time;
}
//125 microseconds is 1/8000th of a second
if ((time - lastRead) > 125) {
lastRead = time;
readMic();
}
}
sendAudio();
}
// Callback for Timer 1
void readMic(void) {
uint16_t value = analogRead(MICROPHONE_PIN);
if (audioEndIdx >= AUDIO_BUFFER_MAX) {
audioEndIdx = 0;
}
audioBuffer[audioEndIdx++] = value;
}
void copyAudio(uint16_t *bufferPtr) {
//if end is after start, read from start->end
//if end is before start, then we wrapped, read from start->max, 0->end
int endSnapshotIdx = audioEndIdx;
bool wrapped = endSnapshotIdx < audioStartIdx;
int endIdx = (wrapped) ? AUDIO_BUFFER_MAX : endSnapshotIdx;
int c = 0;
for(int i=audioStartIdx;i<endIdx;i++) {
// do a thing
bufferPtr[c++] = audioBuffer[i];
}
if (wrapped) {
//we have extra
for(int i=0;i<endSnapshotIdx;i++) {
// do more of a thing.
bufferPtr[c++] = audioBuffer[i];
}
}
//and we're done.
audioStartIdx = audioEndIdx;
if (c < AUDIO_BUFFER_MAX) {
bufferPtr[c] = -1;
}
}
// Callback for Timer 1
void sendAudio(void) {
copyAudio(txBuffer);
int i=0;
uint16_t val = 0;
if (audioClient.connected()) {
write_socket(audioClient, txBuffer);
}
else {
while( (val = txBuffer[i++]) < 65535 ) {
Serial.print(val);
Serial.print(',');
}
Serial.println("DONE");
}
}
// an audio sample is 16bit, we need to convert it to bytes for sending over the network
void write_socket(TCPClient socket, uint16_t *buffer) {
int i=0;
uint16_t val = 0;
int tcpIdx = 0;
uint8_t tcpBuffer[1024];
while( (val = buffer[i++]) < 65535 ) {
if ((tcpIdx+1) >= 1024) {
socket.write(tcpBuffer, tcpIdx);
tcpIdx = 0;
}
tcpBuffer[tcpIdx] = val & 0xff;
tcpBuffer[tcpIdx+1] = (val >> 8);
tcpIdx += 2;
}
// any leftovers?
if (tcpIdx > 0) {
socket.write(tcpBuffer, tcpIdx);
}
}
and the waveRecorder.js file:
// make sure you have Node.js Installed!
// Get the IP address of your photon, and put it here:
// CLI command to get your photon's IP address
//
// particle get MY_DEVICE_NAME ipAddress
// Put your IP here!
var settings = {
ip: "192.168.0.54",
port: 3443
};
/**
* Created by middleca on 7/18/15.
*/
//based on a sample from here
// http://stackoverflow.com/questions/19548755/nodejs-write-binary-data-into-writablestream-with-buffer
var fs = require("fs");
var samplesLength = 1000;
var sampleRate = 8000;
var outStream = fs.createWriteStream("test.wav");
var writeHeader = function() {
var b = new Buffer(1024);
b.write('RIFF', 0);
/* file length */
b.writeUInt32LE(32 + samplesLength * 2, 4);
//b.writeUint32LE(0, 4);
b.write('WAVE', 8);
/* format chunk identifier */
b.write('fmt ', 12);
/* format chunk length */
b.writeUInt32LE(16, 16);
/* sample format (raw) */
b.writeUInt16LE(1, 20);
/* channel count */
b.writeUInt16LE(1, 22);
/* sample rate */
b.writeUInt32LE(sampleRate, 24);
/* byte rate (sample rate * block align) */
b.writeUInt32LE(sampleRate * 2, 28);
/* block align (channel count * bytes per sample) */
b.writeUInt16LE(2, 32);
/* bits per sample */
b.writeUInt16LE(16, 34);
/* data chunk identifier */
b.write('data', 36);
/* data chunk length */
//b.writeUInt32LE(40, samplesLength * 2);
b.writeUInt32LE(0, 40);
outStream.write(b.slice(0, 50));
};
writeHeader(outStream);
var net = require('net');
console.log("connecting...");
client = net.connect(settings.port, settings.ip, function () {
client.setNoDelay(true);
client.on("data", function (data) {
try {
console.log("GOT DATA");
outStream.write(data);
//outStream.flush();
console.log("got chunk of " + data.toString('hex'));
}
catch (ex) {
console.error("Er!" + ex);
}
});
});
setTimeout(function() {
console.log('recorded for 10 seconds');
client.end();
outStream.end();
process.exit(0);
}, 10 * 1000);
Thieme! Such a beginner's question... SO unworthy!
Anyway, I will iron my heart and tell you the answer.
First of all, you misunderstood: the .ino file should go to the Photon and the waveRecorder.js file should be stored on your computer (or server) and called whenever you want to retrieve the audio. As you can read in the code, the .ino file makes sure that every millisecond it will check if something wants to connect, and if so, it will stream the sound to the wav.file stored in the same location as your waveRecorder.js file. "Something wants to connect" happens when you launch waveRecorder.js. Make sure you have node installed.
So, to sum it up:
Download the two files (main.ino and waveRecorder.js) to your computer in a folder ../xx/folderName
Then configure the IPAddress in both files using your photon's IPAddress
Upload main.ino to the photon (type 'particle flash abcdefgh123456578 "xx/../folderName/main.ino"' in the terminal)
Then run waveRecorder.js by typing 'node "xx/../folderName/waveRecorder.js"' in your terminal.
That should do it.. Even I got it working :)
I'm having this little problem with some easy code, basically what I'm doing is sending information via the serial port via a program I wrote in Java. The information is getting their for basic statements (IE, can turn on lights and stuff) but I'm having errors getting it to decode strings with number values send to it.
So for example, I'm sending strings that look like this
BS//:+000/+000/+000
and the decoding method I'm using looks like this.
After adding the string via this:
if (inputString.startsWith("BS//:")) //**fixed
{
inputInfoToBaseStepper(inputString);
baseStepperRunAction(baseStepperRotCount, baseStepperRotStepSize, baseStepperTime);
}
Sends it too...
void inputInfoToBaseStepper(String baseStepper)
{
baseStepperRotCount = baseStepper.substring(6,9).toInt();
baseStepperRotStepSize = baseStepper.substring(10,13).toInt();
baseStepperTime = baseStepper.substring(15,18).toInt();
}
Which should decode and run
void baseStepperRunAction (int rotations, int StepSize, int delayTime)
{
for (int rotations; rotations >=0; rotations--)
{
baseStepper.step(StepSize);
delay(delayTime);
}
}
Problem seems to be that it doesn't decode... ideas I'm sort of lost at this stage. :/
(total past of the code, I know the information is getting there, just not compiling like it should.)
#include <Stepper.h>
//#include <HardwareSerial.h>
// int intensity = 0; // led intensity this is needed just as example for this sketch
String inputString = ""; // a string to hold incoming data (this is general code you can reuse)
boolean stringComplete = false; // whether the string is complete (this is general code you can reuse)
int stepsPerRevolution = 64; //at 5.625 degrees a step
// initialize the stepper library on pins 8 through 11:
Stepper baseStepper(stepsPerRevolution, 2,3,4,5); // protocols start with //BS:
Stepper shoulderStepper(stepsPerRevolution, 6,7,8,9); // protocols start with //SS:
Stepper armStepper(stepsPerRevolution, 10,11,12,13); // protocols start with //AS:
//--------baseStepper--------//
int baseStepperRotCount = 0; //how many rotations in the for loop is needed
int baseStepperRotStepSize = 0; // how large should the steps be...
int baseStepperTime = 0; //delay time needed between each step (delay); so the stepper can do it's work.
//--------shoulderStepper--------//
int shoulderStepperRotCount =0;
void setup() {
// initialize serial: (this is general code you can reuse)
Serial.begin(115200);
}
void loop() {
// when a newline arrives:
if (stringComplete) {
//these are test if statements, they serve no purpose after the intial boot, but must be included to test the connectivity;
if (inputString.startsWith("alpha"))
{
boolean msgRecognized = true;
pinMode(13, OUTPUT);
digitalWrite(13, HIGH);
inputString = "";
stringComplete = false;
}
else if (inputString.startsWith("beta"))
{
boolean msgRecognized = true;
pinMode(13, OUTPUT);
digitalWrite(13, LOW);
inputString = "";
stringComplete = false;
}
//---------------------///
//these statements set the engines and prepare for running of the program.
if (inputString.startsWith("//BS:")) // "BS//:+000/+000/+000"
{
inputInfoToBaseStepper(inputString);
baseStepperRunAction(baseStepperRotCount, baseStepperRotStepSize, baseStepperTime);
}
else if (inputString.startsWith("//SS:"))
{
//inputInfoToShoulderStepper();
//outputConfirmed();
}
else if (inputString.startsWith("//AS:"))
{
//inputInfoToArmStepper();
// outputConfirmed();
}
if(inputString.startsWith("alp://")) { // OK is a message I know (this is general code you can reuse)
boolean msgRecognized = true;
if(inputString.substring(6,10) == "kprs") { // KeyPressed }
msgRecognized = false; // this sketch doesn't know other messages in this case command is ko (not ok)
// Prepare reply message if caller supply a message id (this is general code you can reuse)
int idPosition = inputString.indexOf("?id=");
if(idPosition != -1) {
String id = inputString.substring(idPosition + 4);
// print the reply
Serial.print("alp://rply/");
if(msgRecognized) { // this sketch doesn't know other messages in this case command is ko (not ok)
Serial.print("ok?id=");
} else {
Serial.print("ko?id=");
}
Serial.print(id);
Serial.write(255); // End of Message
Serial.flush();
}
}
// clear the string:
inputString = "";
stringComplete = false;
}
}
}
/*
Send listen messages
int index = 0;
for (index = 0; index < digitalPinListeningNum; index++) {
if(digitalPinListening[index] == true) {
int value = digitalRead(index);
if(value != digitalPinListenedValue[index]) {
digitalPinListenedValue[index] = value;
Serial.print("alp://dred/");
Serial.print(index);
Serial.print("/");
Serial.print(value);
Serial.write(255);
Serial.flush();
}
}
}
for (index = 0; index < analogPinListeningNum; index++) {
if(analogPinListening[index] == true) {
int value = analogRead(index);
if(value != analogPinListenedValue[index]) {
analogPinListenedValue[index] = value;
Serial.print("alp://ared/");
Serial.print(index);
Serial.print("/");
Serial.print(value);
Serial.write(255); // End of Message
Serial.flush();
}
}
}
} */
//this method decodes and stores inputs
void inputInfoToBaseStepper(String baseStepper)
{
baseStepperRotCount = baseStepper.substring(6,9).toInt(); // B S / / : + 0 0 0 / + 0 0 0 / + 0 0 0
baseStepperRotStepSize = baseStepper.substring(10,13).toInt();// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8
baseStepperTime = baseStepper.substring(15,18).toInt();
}
//this method runs the base stepper off the decoded actions.
void baseStepperRunAction (int rotations, int StepSize, int delayTime)
{
for (int rotations; rotations >=0; rotations--)
{
baseStepper.step(StepSize);
delay(delayTime);
}
}
/*
SerialEvent occurs whenever a new data comes in the
hardware serial RX. This routine is run between each
time loop() runs, so using delay inside loop can delay
response. Multiple bytes of data may be available.
This is general code you can reuse.
*/
void serialEvent() {
while (Serial.available() && !stringComplete) {
// get the new byte:
char inChar = (char)Serial.read();
// add it to the inputString:
inputString += inChar;
// if the incoming character is a newline, set a flag
// so the main loop can do something about it:
if (inChar == '\n') {
stringComplete = true;
}
}
}
I'm trying to use the Media Foundation Transforms to convert a HE-AAC audio stream (received by live555, a RTP library), into PCM (to be played via the waveOutXxx Win32 API). However, at present I'm processing pre-recorded packets.
When I call IMFTransform::ProcessOutput, it returns MF_E_TRANSFORM_STREAM_CHANGE.
The documentation for this in _MFT_PROCESS_OUTPUT_STATUS indicates that I should determine the correct streamType, and call IMFTransform::SetOutputType again.
However, I am unable to determine what the correct parameters for SetOutputType.
For reference, the RTSP description of the stream is
m=audio 0 RTP/AVP 97
a=rtpmap:97 mpeg4-generic/16000/1
a=fmtp:97 streamtype=5; profile-level-id=15; mode=AAC-hbr; config=1408;
sizeLength=13; indexLength=3; indexDeltaLength=3; profile=1; bitrate=32000;
My code is (sorry for the length; error handling removed for brevity)
static IMFMediaType *s_inputMediaType;
static IMFMediaType *s_outputMediaType;
static IMFTransform *s_transform;
static DWORD s_outputSampleSize;
static void InitMFT()
{
HRESULT res;
res = CoInitializeEx(NULL, COINIT_APARTMENTTHREADED); // probably should use threaded in production
#if 0
res = MFCreateMediaType(&inputMediaType);
res = MFCreateMediaType(&outputMediaType);
res = inputMediaType->SetGUID(MF_MT_MAJOR_TYPE, MFMediaType_Audio);
res = inputMediaType->SetGUID(MF_MT_SUBTYPE, MFAudioFormat_AAC);
res = inputMediaType->SetUINT32(MF_MT_AAC_AUDIO_PROFILE_LEVEL_INDICATION, 15);
res = inputMediaType->SetUINT32(MF_MT_AAC_PAYLOAD_TYPE, 1); // Audio Data Transport Stream
res = outputMediaType->SetGUID(MF_MT_MAJOR_TYPE, MFMediaType_Audio);
res = outputMediaType->SetGUID(MF_MT_SUBTYPE, MFAudioFormat_PCM);
res = outputMediaType->SetUINT32(MF_MT_SAMPLE_SIZE, 2);
res = outputMediaType->SetUINT32(MF_MT_AUDIO_NUM_CHANNELS, 2);
res = outputMediaType->SetUINT32(MF_MT_AUDIO_BITS_PER_SAMPLE, 8 * 2);
res = outputMediaType->SetUINT32(MF_MT_AUDIO_SAMPLES_PER_SECOND, 8000);
res = outputMediaType->SetUINT32(MF_MT_AUDIO_AVG_BYTES_PER_SECOND, 8000 * 2 *2);
#endif
CLSID *clsids = NULL;
UINT32 clsidCount=0;
MFT_REGISTER_TYPE_INFO inputType = {MFMediaType_Audio, MEDIASUBTYPE_MPEG_HEAAC}; // WAVE_FORMAT_MPEG_HEAAC can't be searched for
MFT_REGISTER_TYPE_INFO outputType = {MFMediaType_Audio, WAVE_FORMAT_PCM}; // MEDIASUBTYPE_PCM doesn't exist (but can be search for if created)
res = MFTEnum(MFT_CATEGORY_AUDIO_DECODER, 0,
&inputType,
NULL, /* &outputType, */ // search fails if outputType is not NULL
NULL, &clsids, &clsidCount);
ListTranscoders(clsids, clsidCount);
res = CoCreateInstance(clsids[0], NULL, CLSCTX_ALL, IID_PPV_ARGS(&s_transform));
/* GetStreamCount and GetStreamIDs always return E_NOTIMPL */
DWORD inputCount;
DWORD outputCount;
res = s_transform->GetStreamCount(&inputCount, &outputCount);
DWORD inputIDs[16];
DWORD outputIDs[16];
res = s_transform->GetStreamIDs(inputCount, inputIDs,
outputCount, outputIDs);
res = s_transform->GetInputAvailableType(0, 0, &s_inputMediaType);
res = s_transform->SetInputType(0, s_inputMediaType, 0);
res = s_transform->GetOutputAvailableType(0, 1, &s_outputMediaType); // 1 here is because PCM outputer is the second output offered in ListTranscoders
res = s_transform->SetOutputType(0, s_outputMediaType, 0);
MFT_OUTPUT_STREAM_INFO outputStreamInfo;
res = s_transform->GetOutputStreamInfo(0, &outputStreamInfo);
s_outputSampleSize = outputStreamInfo.cbSize;
GUID inputCodec;
GUID outputCodec;
res = s_inputMediaType ->GetGUID(MF_MT_SUBTYPE, &inputCodec);
res = s_outputMediaType->GetGUID(MF_MT_SUBTYPE, &outputCodec);
CoTaskMemFree(clsids);
}
/*
http://msdn.microsoft.com/en-us/library/bb530106%28v=vs.85%29.aspx
http://msdn.microsoft.com/en-us/library/bb530123%28v=vs.85%29.aspx
*/
void ProcessData(const void* inputData, DWORD inputSize,
void* outputData, DWORD &outputSize)
{
HRESULT res;
IMFSample *pSample;
IMFMediaBuffer *pBuffer;
BYTE *pData = NULL;
/**** Create an input sample buffer, from the supplied data ****/
res = MFCreateSample(&pSample);
res = MFCreateMemoryBuffer(inputSize, &pBuffer);
res = pBuffer->Lock(&pData, NULL, NULL);
memcpy_s(pData, inputSize, inputData, inputSize);
res = pBuffer->SetCurrentLength(inputSize);
res = pBuffer->Unlock();
res = pSample->AddBuffer(pBuffer);
/**** Create output buffer ****/
IMFSample *pOutputSample;
IMFMediaBuffer *pOutputBuffer;
res = MFCreateSample(&pOutputSample);
res = MFCreateMemoryBuffer(s_outputSampleSize, &pOutputBuffer);
res = pOutputSample->AddBuffer(pOutputBuffer);
MFT_OUTPUT_DATA_BUFFER outputDataBuffer; // can be an array
outputDataBuffer.dwStreamID=0;
outputDataBuffer.pSample=pOutputSample;
outputDataBuffer.dwStatus=0;
outputDataBuffer.pEvents = NULL;
DWORD outputStatus=0;
/*** Process the data, and get it back ****/
res = s_transform->ProcessInput(0, pSample, 0);
res = s_transform->ProcessOutput( MFT_PROCESS_OUTPUT_DISCARD_WHEN_NO_BUFFER,
1, &outputDataBuffer, &outputStatus);
if (res==MF_E_TRANSFORM_STREAM_CHANGE)
{
// http://msdn.microsoft.com/en-us/library/windows/desktop/dd797815%28v=vs.85%29.aspx
// indicates that the output always changes
// but not how to handle it
/* GetStreamCount and GetStreamIDs always return E_NOTIMPL */
DWORD inputCount;
DWORD outputCount;
res = s_transform->GetStreamCount(&inputCount, &outputCount);
DWORD inputIDs[16];
DWORD outputIDs[16];
res = s_transform->GetStreamIDs(inputCount, inputIDs,
outputCount, outputIDs);
res = s_transform->GetInputAvailableType(0, 0, &s_inputMediaType);
res = s_transform->SetInputType(0, s_inputMediaType, 0);
res = s_transform->GetOutputAvailableType(0, 1, &s_outputMediaType); // 1 here is because PCM outputer is the second output offered
res = s_transform->SetOutputType(0, s_outputMediaType, 0);
}
/**** Extract converted audio from the sample ****/
DWORD dwNumOutputBuffers, i;
res = outputDataBuffer.pSample->GetBufferCount(&dwNumOutputBuffers);
for(i=0; i<dwNumOutputBuffers; i++)
{
IMFMediaBuffer *outputBuffer;
res = outputDataBuffer.pSample->GetBufferByIndex(i, &outputBuffer);
BYTE *outData;
DWORD outDataLen = 0;
res = outputBuffer->Lock(&outData, NULL, &outDataLen);
memcpy(outputBuffer, outData, outDataLen);
res = outputBuffer->Unlock();
}
/* TODO: Release any neccessery references */
}
I'm doing a very similar thing. You must do a Set Output Type, otherwise your next processinput will give you MF_E_TRANSFORM_TYPE_NOT_SET,
You must also flush the decoder, otherwise your next process input will return
MF_E_NOTACCEPTING. Working from your code, I got it to work by removing the set input type res adding after this line
res = s_transform->GetOutputAvailableType(0, 1, &s_outputMediaType); // 1 here is because PCM outputer is the second output offered
GUID outputCodec;
res = s_outputMediaType->GetGUID(MF_MT_SUBTYPE, &outputCodec);
if (outputCodec == MFAudioFormat_PCM){
printf("\nDecoder Output is expecting pcm format");
res = s_transform->SetOutputType(0, s_outputMediaType, 0);//setting the type again
}
if (outputCodec == MFAudioFormat_Float){
printf("\nDecoder Output is expecting float pcm format");
}
s_transform->ProcessMessage(MFT_MESSAGE_COMMAND_FLUSH,NULL);
return res;//no output coming get another input to process.
For testing, I was using samples that came from a source reader to a good file. Be sure to play many packets, as some packets containing 'quiet' can be short and all 0.
Also, thus url http://msdn.microsoft.com/en-us/library/windows/desktop/ff485864%28v=vs.85%29.aspx nicely specifies how to configure the output. It is very useful.
Reading closely http://msdn.microsoft.com/en-us/library/windows/desktop/dd742784%28v=vs.85%29.aspx you can notice that some of the attributes (MF_MT_AUDIO_BITS_PER_SAMPLE) on the input media type tell the decoder how the output is to be formatted. If you are able to more fully specify the desired input and output you can avoid the stream change. Your 1st sample could instead be MF_E_TRANSFORM_NEED_MORE_INPUT .
I wrote a solution that involved OpenProcess, EnumProcessModules, GetModuleInformation and GetModuleBaseName, but apparently EnumProcessModules and GetModuleBaseName do not exist in Windows CE! What alternative is there?
I found a way to do this with CreateToolhelp32Snapshot, Module32First, Module32Next, Process32First and Process32Next. First you have to get a list of modules, then search through the list of modules to find the desired address.
#include <Tlhelp32.h>
struct MyModuleInfo
{
BYTE* Base;
HMODULE Handle;
DWORD Size;
enum { MaxNameLen = 36 };
TCHAR Name[MaxNameLen];
};
bool GetModuleList(vector<MyModuleInfo>& moduleList)
{
HANDLE hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS | TH32CS_SNAPMODULE | TH32CS_GETALLMODS, 0);
if (hSnapshot == INVALID_HANDLE_VALUE)
return false;
MODULEENTRY32 moduleInfo;
moduleInfo.dwSize = sizeof(moduleInfo);
if (Module32First(hSnapshot, &moduleInfo)) do {
MyModuleInfo myInfo;
myInfo.Handle = moduleInfo.hModule;
myInfo.Base = moduleInfo.modBaseAddr;
myInfo.Size = moduleInfo.modBaseSize;
memcpy(myInfo.Name, moduleInfo.szModule, min(sizeof(myInfo.Name), sizeof(moduleInfo.szModule)));
myInfo.Name[myInfo.MaxNameLen-1] = '\0';
moduleList.push_back(myInfo);
} while (Module32Next(hSnapshot, &moduleInfo));
// The module list obtained above only contains DLLs! To get the EXE files
// also, we must call Process32First and Process32Next in a loop.
PROCESSENTRY32 processInfo;
processInfo.dwSize = sizeof(processInfo);
if (Process32First(hSnapshot, &processInfo)) do {
MyModuleInfo myInfo;
myInfo.Handle = NULL; // No handle given
myInfo.Base = (BYTE*)processInfo.th32MemoryBase;
myInfo.Size = 0x800000; // No size provided! Allow max 8 MB
memcpy(myInfo.Name, processInfo.szExeFile, min(sizeof(myInfo.Name), sizeof(processInfo.szExeFile)));
myInfo.Name[myInfo.MaxNameLen-1] = '\0';
moduleList.push_back(myInfo);
} while(Process32Next(hSnapshot, &processInfo));
// Debug output
for (int i = 0; i < (int)moduleList.size(); i++) {
MyModuleInfo& m = moduleList[i];
TRACE(_T("%-30s: 0x%08x - 0x%08x\n"), m.Name, (DWORD)m.Base, (DWORD)m.Base + m.Size);
}
CloseToolhelp32Snapshot(hSnapshot);
return true;
}
const MyModuleInfo* GetModuleForAddress(vector<MyModuleInfo>& moduleList, void* address)
{
for (int m = 0; m < (int)moduleList.size(); m++) {
const MyModuleInfo& mInfo = moduleList[m];
if (address >= mInfo.Base && address < mInfo.Base + mInfo.Size)
return &mInfo;
}
return NULL;
}