playing wav audio file in C++ and QT - c++

I am reading a .wav file in C and then I am trying to play the audio file using some of the QT functions. Here is how I read the file:
FILE *fhandle=fopen("myAudioFile.wav","rb");
fread(ChunkID,1,4,fhandle);
fread(&ChunkSize,4,1,fhandle);
fread(Format,1,4,fhandle);
fread(Subchunk1ID,1,4,fhandle);
fread(&Subchunk1Size,4,1,fhandle);
fread(&AudioFormat,2,1,fhandle);
fread(&NumChannels,2,1,fhandle);
fread(&SampleRate,4,1,fhandle);
fread(&ByteRate,4,1,fhandle);
fread(&BlockAlign,2,1,fhandle);
fread(&BitsPerSample,2,1,fhandle);
fread(&Subchunk2ID,1,4,fhandle);
fread(&Subchunk2Size,4,1,fhandle);
Data=new quint16 [Subchunk2Size/(BitsPerSample/8)];
fread(Data,BitsPerSample/8,Subchunk2Size/(BitsPerSample/8),fhandle);
fclose(fhandle);
So my audio file is inside Data. Each element of Data is unsigned 16-bit Integer.
To play the sound I divide each 16-bit unsigned Integer into two characters and then every 3 ms (using a timer) I send 256 characters to the audio card.
Assume myData is a character array of 256 characters I do this (every 3 ms) to play the sound:
m_output->write(myData, 256);
Also m_output is defined as:
m_output = m_audioOutput->start();
and m_audioOutput is defined as:
m_audioOutput = new QAudioOutput(m_Outputdevice, m_format, this);
And the audio format is set correctly as:
m_format.setFrequency(44100);
m_format.setChannels(2);
m_format.setSampleSize(16);
m_format.setSampleType(QAudioFormat::UnSignedInt );
m_format.setByteOrder(QAudioFormat::LittleEndian);
m_format.setCodec("audio/pcm");
However, when I try to run the code I hear some noise which is very different from the real audio file.
Is there anything I am doing wronge?
Thanks,
TJ

I think the problem is that you are using QTimer. QTimer is absolutely not going to allow you to run code every three milliseconds exactly, regardless of the platform you're using. And if you're off by just one sample, your audio is going to sound horrible. According to the QTimer docs:
...they are not guaranteed to time out at the exact value specified. In
many situations, they may time out late by a period of time that
depends on the accuracy of the system timers.
and
...the accuracy of the timer will not equal [1 ms] in many real-world situations.
As much as I love Qt, I wouldn't try to use it for signal processing. I would use another framework such as JUCE.

Related

MIDI file events to "real-time" without PPQ or SMPTE in header

I am writing simple c++ synthesizer with MIDI playback. I've already implemented playback, but in some midi files information about PPQ or SMPTE(or data invalid, eg. all data bytes is 0) is absent and if i use "default" values of PPQ(ex. 24) and tempo from event(in this files tempo event is only one) playback is too slow or too fast. In this case i correct this value by hand. But if I import this midi in any DAW, they read file correctly and play melody with target BPM.
How to correctly convert events tick to real-time in this case? What am I missing and what do DAWs do in this case?
The ticks-per-quarter-note value is part of the header chunk, so it is present in every file.
If this value is zero, then the file is invalid and cannot be played at all.
For tempo and time signature, the default values are defined in the SMF specification:
All MIDI Files should specify tempo and time signature. If they don't, the time signature is assumed to be 4/4, and the tempo 120 beats per minute.
(120 BPM is the same as a tempo value of 500,000 microseconds per quarter note.)

SDL_Mixer is playing single chunk over itself possible?

I'm having trouble with SDL_Mixer (my lack of experience). Chunks and Music play just fine (using Mix_PlayChannel and Mix_PlayMusic), and playing two different chunks simultaneously isn't an issue.
My problem is that I would like to play some chunk1, and then play second iteration of chunk1 overlapping the first. I am trying to play a single chunk in rapid succession, but it instead plays the sound repeatedly at a much longer interval (not as quickly as I want). I've tested console output and my method of playing/looping is not at fault, since I can see console messages printing, looped at the right speed.
I have an array of Chunks that I periodically load during initialization, using Mix_LoadWAV();
Mix_Chunk *sounds[32];
I also have a function reserved for playing these chunks:
void PlaySound(int snd_id)
{
if(snd_id >= 0 && snd_id < 32)
{
if(Mix_PlayChannel(-1, sounds[snd_id], 0) == -1)
{
printf("Mix_PlayChannel: %s\n",Mix_GetError());
}
}
}
Attempting to play a single sound several times in rapid succession(say, 100ms delay/10bps), I am given the sound playing at a set, slower interval(some 500ms or so/2bps) despite the function being called at 10bps.
I already used "Mix_AllocateChannels(16);" to ensure I have allocated channels (let me know if I'm using that incorrectly) and still, a single chunk from the array refuses to play at a certain rate.
Any ideas/help is appreciated, as well as critique on how I posted this question.
As said in the documentation of SDL_Mixer (https://www.libsdl.org/projects/SDL_mixer/docs/SDL_mixer_28.html) :
"... -1 for the first free unreserved channel."
So if your chunk is longer than 1.6 seconds (16 channels*100ms) you'll run out of channels after 1.6 seconds, and so you wont be enabled to play new chunks until one of the channels end playing.
So there are basically 2 solutions :
Allocate more channels (more than : ChunkDuration (in sec) / Delay (in sec))
Stop a channel, so that you can use it. (and to do it properly, you should not use -1 as channel but a variable that you increment each time you play a chunk (don't forget to set it back to 0 when it's equal to your number of channels) )

Samplesize 8 or 16?

I try to record sound from my pc audio device. I can hear the recorded sound only when I set samplesize to 8 but when I set the samplesize to 16 , the recorded sound is only a whistle. why?
here is the code
void audioprocess::startRecording()
{
outputFile.setFileName("C:/Users/Rem/Documents/Qtproject/remaudio.wav");
outputFile.open( QIODevice::WriteOnly );
QAudioFormat format;
// set up the format you want, eg.
format.setCodec("audio/pcm");
format.setSampleRate(8000);
format.setChannelCount(1);
format.setSampleSize(8);
format.setByteOrder(QAudioFormat::LittleEndian);
format.setSampleType(QAudioFormat::UnSignedInt);
audioInput = new QAudioInput(format);
QAudioDeviceInfo info = QAudioDeviceInfo::defaultInputDevice();
if (!info.isFormatSupported(format)) {
qWarning()<<"default format not supported try to use nearest";
format = info.nearestFormat(format);
}
QTimer::singleShot(120000, this, SLOT(stopRecording()));
audioInput->start(&outputFile);
// Records audio for 120000ms
}
The whistle is probably because every second sample is zero. That means you get a periodic signal at exactly half the sample frequency.
Now what does your code actually do? You haven't shown us the definition of outputFile, but the code snippet is almost literally taken from Qt documentation. And it defines outputFile as a QFile. The file there is called test.raw, and for good reason. Raw files lack a header. Thus it's impossible to determine their sample size.
Finally a question that's in my area of expertise. When it comes to audio development, there are a lot of factors to include into how to program an application. One of those specifically is hardware capabilities. My guess is that your audio device is being sampled too quickly and the higher end bits are just random data.
I would try to increase the sample size, the actual number of frames of audio that are being sampled before being processed, to allow the system to actually buffer the audio and process it while the audio device is receiving new audio input.
Another problem may be the Endianness of your processor. This would usually be an issue if you are on a PowerPC. Or developing for an embedded system that runs on a big Endian processor.
I'm going to take a wild guess here that you are leaving the sample type as unsigned int and only setting the bit depth to 16. Conventionally, 8 bit waveforms are unsigned while 16 bits are signed. Try patching in the following:
format.setSampleSize(16);
format.setSampleType(QAudioFormat::SignedInt);

Rendering some sound data into one new sound data?

I'm creating an application that will read a unique format that contains sound "bank" and offsets when the sound must be played.
Imagine something like..
Sound bank: (ID on the left-hand and file name on the right-hand side)
0 kick.wav
1 hit.wav
2 flute.wav
And the offsets: (Time in ms on the left-hand and sound ID on the right-hand side)
1000 0
2000 1
3000 2
And the application will generate a new sound file (ie. wav, for later conversion to other formats) that plays a kick at first sec, a hit at second sec, and flute at third sec.
I completely have no idea on where to begin.
I usually use FMOD for audio playbacks, but never did something like this before.
I'm using C++ and wxWidgets on a MSVC++ Express Edition environment, and LGPL libraries would be fine.
If I understand correctly, you want to generate a new wave file by mixing wavs from a soundbank. You may not need a sound API at all for this, especially if all your input wavs are in the same format.
Simply load each wav file into a buffer. For SampleRate*secondsUntilStartTime samples, for each buffer in the ActiveList, add buffer[bufferIdx++] into the output buffer. If bufferIdx == bufferLen, remove this buffer from the ActiveList. At StartTime, add the next buffer the ActiveList, and repeat.
If FMOD supports output to a file instead of the sound hardware, you can do this same thing with the streaming API. Just keep track of elapsed samples in the StreamCallback, and start mixing in new files whenever you reach their start offsets.

How to use ALSA's snd_pcm_writei()?

Can someone explain how snd_pcm_writei
snd_pcm_sframes_t snd_pcm_writei(snd_pcm_t *pcm, const void *buffer,
snd_pcm_uframes_t size)
works?
I have used it like so:
for (int i = 0; i < 1; i++) {
f = snd_pcm_writei(handle, buffer, frames);
...
}
Full source code at http://pastebin.com/m2f28b578
Does this mean, that I shouldn't give snd_pcm_writei() the number of
all the frames in buffer, but only
sample_rate * latency = frames
?
So if I e.g. have:
sample_rate = 44100
latency = 0.5 [s]
all_frames = 100000
The number of frames that I should give to snd_pcm_writei() would be
sample_rate * latency = frames
44100*0.5 = 22050
and the number of iterations the for-loop should be?:
(int) 100000/22050 = 4; with frames=22050
and one extra, but only with
100000 mod 22050 = 11800
frames?
Is that how it works?
Louise
http://www.alsa-project.org/alsa-doc/alsa-lib/group___p_c_m.html#gf13067c0ebde29118ca05af76e5b17a9
frames should be the number of frames (samples) you want to write from the buffer. Your system's sound driver will start transferring those samples to the sound card right away, and they will be played at a constant rate.
The latency is introduced in several places. There's latency from the data buffered by the driver while waiting to be transferred to the card. There's at least one buffer full of data that's being transferred to the card at any given moment, and there's buffering on the application side, which is what you seem to be concerned about.
To reduce latency on the application side you need to write the smallest buffer that will work for you. If your application performs a DSP task, that's typically one window's worth of data.
There's no advantage in writing small buffers in a loop - just go ahead and write everything in one go - but there's an important point to understand: to minimize latency, your application should write to the driver no faster than the driver is writing data to the sound card, or you'll end up piling up more data and accumulating more and more latency.
For a design that makes producing data in lockstep with the sound driver relatively easy, look at jack (http://jackaudio.org/) which is based on registering a callback function with the sound playback engine. In fact, you're probably just better off using jack instead of trying to do it yourself if you're really concerned about latency.
I think the reason for the "premature" device closure is that you need to call snd_pcm_drain(handle); prior to snd_pcm_close(handle); to ensure that all data is played before the device is closed.
I did some testing to determine why snd_pcm_writei() didn't seem to work for me using several examples I found in the ALSA tutorials and what I concluded was that the simple examples were doing a snd_pcm_close () before the sound device could play the complete stream sent it to it.
I set the rate to 11025, used a 128 byte random buffer, and for looped snd_pcm_writei() for 11025/128 for each second of sound. Two seconds required 86*2 calls snd_pcm_write() to get two seconds of sound.
In order to give the device sufficient time to convert the data to audio, I put used a for loop after the snd_pcm_writei() loop to delay execution of the snd_pcm_close() function.
After testing, I had to conclude that the sample code didn't supply enough samples to overcome the device latency before the snd_pcm_close function was called which implies that the close function has less latency than the snd_pcm_write() function.
If the ALSA driver's start threshold is not set properly (if in your case it is about 2s), then you will need to call snd_pcm_start() to start the data rendering immediately after snd_pcm_writei().
Or you may set appropriate threshold in the SW params of ALSA device.
ref:
http://www.alsa-project.org/alsa-doc/alsa-lib/group___p_c_m.html
http://www.alsa-project.org/alsa-doc/alsa-lib/group___p_c_m___s_w___params.html