1/*
2 * Copyright 2009, Krzysztof ��wiertnia (krzysiek.bmkx_gmail_com).
3 * All Rights Reserved. Distributed under the terms of the MIT License.
4 */
5
6
7#include <stdio.h>
8#include <stdlib.h>
9#include <string.h>
10
11#include <Entry.h>
12#include <InterfaceDefs.h>
13#include <MediaFile.h>
14#include <MediaTrack.h>
15#include <PushGameSound.h>
16
17
18int
19main(int argc, char *argv[])
20{
21	// 256 frames * 4 buffer parts * 2 channels * 2 bytes per sample
22	// will give us internal buffer of 4096 bytes
23	size_t framesPerBufferPart = 256;
24	size_t bufferPartCount = 4;
25
26	if (argc != 2 && argc != 4) {
27		printf("Usage: %s <sound file name> [<frames per part> <parts>]\n",
28			argv[0]);
29		return 0;
30	}
31
32	if (argc == 4) {
33		size_t size = strtoul(argv[2], NULL, 10);
34		if (size > 0)
35			framesPerBufferPart = size;
36
37		size = strtoul(argv[3], NULL,  10);
38		if (size == 1) {
39			printf("at least 2 buffer parts are needed\n");
40			return 1;
41		}
42		if (size > 0)
43			bufferPartCount = size;
44	}
45
46	printf("frames per buffer part: %ld\n", framesPerBufferPart);
47	printf("buffer part count: %ld\n", bufferPartCount);
48
49	BEntry entry(argv[1]);
50	if (entry.InitCheck() != B_OK || !entry.Exists()) {
51		printf("cannot open input file\n");
52		return 1;
53	}
54
55	entry_ref entryRef;
56	entry.GetRef(&entryRef);
57
58	BMediaFile mediaFile(&entryRef);
59	if (mediaFile.InitCheck() != B_OK) {
60		printf("file not supported\n");
61		return 1;
62	}
63
64	if (mediaFile.CountTracks() == 0) {
65		printf("no tracks found in file\n");
66		return 1;
67	}
68
69	BMediaTrack *mediaTrack = mediaFile.TrackAt(0);
70	if (mediaTrack == NULL) {
71		printf("problem getting track from file\n");
72		return 1;
73	}
74
75	// propose format, let it decide frame rate, channels number and buf size
76	media_format format;
77	memset(&format, 0, sizeof(format));
78	format.type = B_MEDIA_RAW_AUDIO;
79	format.u.raw_audio.format = media_raw_audio_format::B_AUDIO_SHORT;
80	format.u.raw_audio.byte_order = B_MEDIA_LITTLE_ENDIAN;
81
82	if (mediaTrack->DecodedFormat(&format) != B_OK) {
83		printf("cannot set decoder output format\n");
84		return 1;
85	}
86
87	printf("negotiated format:\n");
88	printf("frame rate: %g Hz\n", format.u.raw_audio.frame_rate);
89	printf("channel count: %ld\n", format.u.raw_audio.channel_count);
90	printf("buffer size: %ld bytes\n", format.u.raw_audio.buffer_size);
91
92	gs_audio_format gsFormat;
93	memset(&gsFormat, 0, sizeof(gsFormat));
94	gsFormat.frame_rate = format.u.raw_audio.frame_rate;
95	gsFormat.channel_count = format.u.raw_audio.channel_count;
96	gsFormat.format = format.u.raw_audio.format;
97	gsFormat.byte_order = format.u.raw_audio.byte_order;
98
99	BPushGameSound pushGameSound(framesPerBufferPart, &gsFormat,
100		bufferPartCount);
101	if (pushGameSound.InitCheck() != B_OK) {
102		printf("trouble initializing push game sound: %s\n",
103			strerror(pushGameSound.InitCheck()));
104		return 1;
105	}
106
107	uint8 *buffer;
108	size_t bufferSize;
109	if (pushGameSound.LockForCyclic((void **)&buffer, &bufferSize)
110			!= BPushGameSound::lock_ok) {
111		printf("cannot lock buffer\n");
112		return 1;
113	}
114	memset(buffer, 0, bufferSize);
115
116	if (pushGameSound.StartPlaying() != B_OK) {
117		printf("cannot start playback\n");
118		return 1;
119	}
120
121	printf("playing, press [esc] to exit...\n");
122
123	uint8 decoded[format.u.raw_audio.buffer_size * 2];
124	size_t bufferPartSize = framesPerBufferPart
125		* format.u.raw_audio.channel_count
126		* (format.u.raw_audio.format
127			& media_raw_audio_format::B_AUDIO_SIZE_MASK);
128	size_t decodedSize = 0;
129	size_t partPos = 0;
130	size_t pos = 0; /*pushGameSound.CurrentPosition();*/
131	key_info keyInfo;
132
133	while (true) {
134		// fill buffer part with data from decoded buffer
135		while (partPos < bufferPartSize && decodedSize) {
136			size_t size = min_c(bufferPartSize - partPos, decodedSize);
137
138			memcpy(buffer + pos + partPos, decoded, size);
139			partPos += size;
140
141			decodedSize -= size;
142			memmove(decoded, decoded + size, decodedSize);
143		}
144
145		// if there are too little data to fill next buffer part
146		// read next decoded frames
147		if (partPos < bufferPartSize) {
148			int64 frameCount;
149			if (mediaTrack->ReadFrames(decoded + decodedSize, &frameCount)
150					!= B_OK)
151				break;
152			if (frameCount == 0)
153				break;
154
155			decodedSize += frameCount * format.u.raw_audio.channel_count
156				* (format.u.raw_audio.format
157					& media_raw_audio_format::B_AUDIO_SIZE_MASK);
158
159			printf("\rtime: %.2f",
160				(double)mediaTrack->CurrentTime() / 1000000LL);
161			fflush(stdout);
162
163			continue;
164		}
165
166		// this buffer part is done
167		partPos = 0;
168		pos += bufferPartSize;
169		if (bufferSize <= pos)
170			pos = 0;
171
172		// playback sync - wait for the buffer part we're about to fill to be
173		// played
174		while (pushGameSound.CurrentPosition() >= pos + bufferPartSize
175			|| pushGameSound.CurrentPosition() < pos)
176			snooze(1000 * framesPerBufferPart / gsFormat.frame_rate);
177
178		// check escape key state
179		if (get_key_info(&keyInfo) != B_OK) {
180			printf("\nkeyboard state read error\n");
181			break;
182		}
183		if ((keyInfo.key_states[0] & 0x40) != 0)
184			break;
185	}
186
187	pushGameSound.StopPlaying();
188
189	mediaFile.ReleaseTrack(mediaTrack);
190	mediaFile.CloseFile();
191
192	printf("\nfinished.\n");
193
194	return 0;
195}
196